Subversion-Projekte lars-tiefland.laravel_shop

Revision

Revision 148 | Details | Vergleich mit vorheriger | 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
 *
200 lars 6
 * (c) 2012-2023 Justin Hileman
148 lars 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
    public static function isSupported(): bool
30
    {
31
        // runkit_import was removed in runkit7-4.0.0a1
32
        return \extension_loaded('runkit') || \extension_loaded('runkit7') && \function_exists('runkit_import');
33
    }
34
 
35
    /**
36
     * Construct a Runkit Reloader.
37
     *
38
     * @todo Pass in Parser Factory instance for dependency injection?
39
     */
40
    public function __construct()
41
    {
42
        $parserFactory = new ParserFactory();
43
        $this->parser = $parserFactory->createParser();
44
    }
45
 
46
    /**
47
     * Reload code on input.
48
     *
49
     * @param Shell  $shell
50
     * @param string $input
51
     */
52
    public function onInput(Shell $shell, string $input)
53
    {
54
        $this->reload($shell);
55
    }
56
 
57
    /**
58
     * Look through included files and update anything with a new timestamp.
59
     *
60
     * @param Shell $shell
61
     */
62
    private function reload(Shell $shell)
63
    {
64
        \clearstatcache();
65
        $modified = [];
66
 
67
        foreach (\get_included_files() as $file) {
68
            $timestamp = \filemtime($file);
69
 
70
            if (!isset($this->timestamps[$file])) {
71
                $this->timestamps[$file] = $timestamp;
72
                continue;
73
            }
74
 
75
            if ($this->timestamps[$file] === $timestamp) {
76
                continue;
77
            }
78
 
79
            if (!$this->lintFile($file)) {
80
                $msg = \sprintf('Modified file "%s" could not be reloaded', $file);
81
                $shell->writeException(new ParseErrorException($msg));
82
                continue;
83
            }
84
 
85
            $modified[] = $file;
86
            $this->timestamps[$file] = $timestamp;
87
        }
88
 
89
        // switch (count($modified)) {
90
        //     case 0:
91
        //         return;
92
 
93
        //     case 1:
94
        //         printf("Reloading modified file: \"%s\"\n", str_replace(getcwd(), '.', $file));
95
        //         break;
96
 
97
        //     default:
98
        //         printf("Reloading %d modified files\n", count($modified));
99
        //         break;
100
        // }
101
 
102
        foreach ($modified as $file) {
103
            $flags = (
104
                RUNKIT_IMPORT_FUNCTIONS |
105
                RUNKIT_IMPORT_CLASSES |
106
                RUNKIT_IMPORT_CLASS_METHODS |
107
                RUNKIT_IMPORT_CLASS_CONSTS |
108
                RUNKIT_IMPORT_CLASS_PROPS |
109
                RUNKIT_IMPORT_OVERRIDE
110
            );
111
 
112
            // these two const cannot be used with RUNKIT_IMPORT_OVERRIDE  in runkit7
113
            if (\extension_loaded('runkit7')) {
114
                $flags &= ~RUNKIT_IMPORT_CLASS_PROPS & ~RUNKIT_IMPORT_CLASS_STATIC_PROPS;
115
                runkit7_import($file, $flags);
116
            } else {
117
                runkit_import($file, $flags);
118
            }
119
        }
120
    }
121
 
122
    /**
123
     * Should this file be re-imported?
124
     *
125
     * Use PHP-Parser to ensure that the file is valid PHP.
126
     *
127
     * @param string $file
128
     */
129
    private function lintFile(string $file): bool
130
    {
131
        // first try to parse it
132
        try {
133
            $this->parser->parse(\file_get_contents($file));
134
        } catch (\Throwable $e) {
135
            return false;
136
        }
137
 
138
        return true;
139
    }
140
}