Failed Conditions
Push — experimental/sf ( 099fe7...d58f47 )
by Ryo
110:32 queued 103:08
created

ORM/Mapping/Driver/ReloadSafeAnnotationDriver.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/*
4
 * This file is part of EC-CUBE
5
 *
6
 * Copyright(c) LOCKON CO.,LTD. All Rights Reserved.
7
 *
8
 * http://www.lockon.co.jp/
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Eccube\Doctrine\ORM\Mapping\Driver;
15
16
use Doctrine\ORM\Mapping\MappingException;
17
use Eccube\Util\StringUtil;
18
use PhpCsFixer\Tokenizer\Token;
19
use PhpCsFixer\Tokenizer\Tokens;
20
21
/**
22
 * 同じプロセス内で新しく生成されたProxyクラスからマッピングメタデータを抽出するためのAnnotationDriver.
23
 *
24
 * 同じプロセス内で、Proxy元のEntityがロードされた後に同じFQCNを持つProxyをロードしようとすると、Fatalエラーが発生する.
25
 * このエラーを回避するために、新しく生成されたProxyクラスは一時的にクラス名を変更してからロードして、マッピングメタデータを抽出する.
26
 */
27
class ReloadSafeAnnotationDriver extends AnnotationDriver
28
{
29
    /**
30
     * @var array 新しく生成されたProxyファイルのリスト
31
     */
32
    protected $newProxyFiles;
33
34
    protected $outputDir;
35
36
    public function setNewProxyFiles($newProxyFiles)
37
    {
38
        $this->newProxyFiles = $newProxyFiles;
39
    }
40
41
    /**
42
     * @param string $outputDir
43
     */
44
    public function setOutputDir($outputDir)
45
    {
46
        $this->outputDir = $outputDir;
47
    }
48
49
    /**
50
     * {@inheritdoc}
51
     */
52
    public function getAllClassNames()
53
    {
54
        if ($this->classNames !== null) {
55
            return $this->classNames;
56
        }
57
58
        if (!$this->paths) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->paths of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
59
            throw MappingException::pathRequired();
60
        }
61
62
        foreach ($this->paths as $path) {
63
            if (!is_dir($path)) {
64
                throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
65
            }
66
67
            $iterator = new \RegexIterator(
68
                new \RecursiveIteratorIterator(
69
                    new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS),
70
                    \RecursiveIteratorIterator::LEAVES_ONLY
71
                ),
72
                '/^.+'.preg_quote($this->fileExtension).'$/i',
73
                \RecursiveRegexIterator::GET_MATCH
74
            );
75
76
            foreach ($iterator as $file) {
77
                $sourceFile = $file[0];
78
79
                if (!preg_match('(^phar:)i', $sourceFile)) {
80
                    $sourceFile = realpath($sourceFile);
81
                }
82
83 View Code Duplication
                foreach ($this->excludePaths as $excludePath) {
84
                    $exclude = str_replace('\\', '/', realpath($excludePath));
85
                    $current = str_replace('\\', '/', $sourceFile);
86
87
                    if (strpos($current, $exclude) !== false) {
88
                        continue 2;
89
                    }
90
                }
91
92
                $proxyFile = str_replace($path, $this->trait_proxies_directory, $sourceFile);
93
                if (file_exists($proxyFile)) {
94
                    $sourceFile = $proxyFile;
95
                }
96
97
                $this->classNames = array_merge($this->classNames ?: [], $this->getClassNamesFromTokens($sourceFile));
0 ignored issues
show
Documentation Bug introduced by
It seems like array_merge($this->class...romTokens($sourceFile)) of type array is incompatible with the declared type array<integer,string>|null of property $classNames.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
98
            }
99
        }
100
101
        return $this->classNames;
102
    }
103
104
    /**
105
     * ソースコードを字句解析してクラス名を解決します.
106
     * 新しく生成されたProxyクラスの場合は、一時的にクラス名を変更したクラスを生成してロードします.
107
     *
108
     * @param $sourceFile string ソースファイル
109
     *
110
     * @return array ソースファイルに含まれるクラス名のリスト
111
     */
112
    private function getClassNamesFromTokens($sourceFile)
113
    {
114
        $tokens = Tokens::fromCode(file_get_contents($sourceFile));
115
        $results = [];
116
        $currentIndex = 0;
117
        while ($currentIndex = $tokens->getNextTokenOfKind($currentIndex, [[T_CLASS]])) {
118
            $classNameTokenIndex = $tokens->getNextMeaningfulToken($currentIndex);
119
            if ($classNameTokenIndex) {
120
                $namespaceIndex = $tokens->getNextTokenOfKind(0, [[T_NAMESPACE]]);
121
                if ($namespaceIndex) {
122
                    $namespaceEndIndex = $tokens->getNextTokenOfKind($namespaceIndex, [';']);
123
                    $namespace = $tokens->generatePartialCode($tokens->getNextMeaningfulToken($namespaceIndex), $tokens->getPrevMeaningfulToken($namespaceEndIndex));
124
                    $className = $tokens[$classNameTokenIndex]->getContent();
125
                    $fqcn = $namespace.'\\'.$className;
126
                    if (class_exists($fqcn) && !$this->isTransient($fqcn)) {
127
                        if (in_array($sourceFile, $this->newProxyFiles)) {
128
                            $newClassName = $className.StringUtil::random(12);
129
                            $tokens[$classNameTokenIndex] = new Token([T_STRING, $newClassName]);
130
                            $newFilePath = $this->outputDir."${newClassName}.php";
131
                            file_put_contents($newFilePath, $tokens->generateCode());
132
                            require_once $newFilePath;
133
                            $results[] = $namespace."\\${newClassName}";
134
                        } else {
135
                            $results[] = $fqcn;
136
                        }
137
                    }
138
                }
139
            }
140
            $currentIndex++;
141
        }
142
143
        return $results;
144
    }
145
}
146