Subversion-Projekte lars-tiefland.laravel_shop

Revision

Zur aktuellen Revision | Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
/*
4
 * This file is part of Psy Shell.
5
 *
6
 * (c) 2012-2022 Justin Hileman
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
 
12
namespace Psy\ExecutionLoop;
13
 
14
use Psy\Exception\ParseErrorException;
15
use Psy\ParserFactory;
16
use Psy\Shell;
17
 
18
/**
19
 * A runkit-based code reloader, which is pretty much magic.
20
 */
21
class RunkitReloader extends AbstractListener
22
{
23
    private $parser;
24
    private $timestamps = [];
25
 
26
    /**
27
     * Only enabled if Runkit is installed.
28
     *
29
     * @return bool
30
     */
31
    public static function isSupported(): bool
32
    {
33
        // runkit_import was removed in runkit7-4.0.0a1
34
        return \extension_loaded('runkit') || \extension_loaded('runkit7') && \function_exists('runkit_import');
35
    }
36
 
37
    /**
38
     * Construct a Runkit Reloader.
39
     *
40
     * @todo Pass in Parser Factory instance for dependency injection?
41
     */
42
    public function __construct()
43
    {
44
        $parserFactory = new ParserFactory();
45
        $this->parser = $parserFactory->createParser();
46
    }
47
 
48
    /**
49
     * Reload code on input.
50
     *
51
     * @param Shell  $shell
52
     * @param string $input
53
     */
54
    public function onInput(Shell $shell, string $input)
55
    {
56
        $this->reload($shell);
57
    }
58
 
59
    /**
60
     * Look through included files and update anything with a new timestamp.
61
     *
62
     * @param Shell $shell
63
     */
64
    private function reload(Shell $shell)
65
    {
66
        \clearstatcache();
67
        $modified = [];
68
 
69
        foreach (\get_included_files() as $file) {
70
            $timestamp = \filemtime($file);
71
 
72
            if (!isset($this->timestamps[$file])) {
73
                $this->timestamps[$file] = $timestamp;
74
                continue;
75
            }
76
 
77
            if ($this->timestamps[$file] === $timestamp) {
78
                continue;
79
            }
80
 
81
            if (!$this->lintFile($file)) {
82
                $msg = \sprintf('Modified file "%s" could not be reloaded', $file);
83
                $shell->writeException(new ParseErrorException($msg));
84
                continue;
85
            }
86
 
87
            $modified[] = $file;
88
            $this->timestamps[$file] = $timestamp;
89
        }
90
 
91
        // switch (count($modified)) {
92
        //     case 0:
93
        //         return;
94
 
95
        //     case 1:
96
        //         printf("Reloading modified file: \"%s\"\n", str_replace(getcwd(), '.', $file));
97
        //         break;
98
 
99
        //     default:
100
        //         printf("Reloading %d modified files\n", count($modified));
101
        //         break;
102
        // }
103
 
104
        foreach ($modified as $file) {
105
            $flags = (
106
                RUNKIT_IMPORT_FUNCTIONS |
107
                RUNKIT_IMPORT_CLASSES |
108
                RUNKIT_IMPORT_CLASS_METHODS |
109
                RUNKIT_IMPORT_CLASS_CONSTS |
110
                RUNKIT_IMPORT_CLASS_PROPS |
111
                RUNKIT_IMPORT_OVERRIDE
112
            );
113
 
114
            // these two const cannot be used with RUNKIT_IMPORT_OVERRIDE  in runkit7
115
            if (\extension_loaded('runkit7')) {
116
                $flags &= ~RUNKIT_IMPORT_CLASS_PROPS & ~RUNKIT_IMPORT_CLASS_STATIC_PROPS;
117
                runkit7_import($file, $flags);
118
            } else {
119
                runkit_import($file, $flags);
120
            }
121
        }
122
    }
123
 
124
    /**
125
     * Should this file be re-imported?
126
     *
127
     * Use PHP-Parser to ensure that the file is valid PHP.
128
     *
129
     * @param string $file
130
     *
131
     * @return bool
132
     */
133
    private function lintFile(string $file): bool
134
    {
135
        // first try to parse it
136
        try {
137
            $this->parser->parse(\file_get_contents($file));
138
        } catch (\Throwable $e) {
139
            return false;
140
        }
141
 
142
        return true;
143
    }
144
}