Failed Conditions
Pull Request — experimental/3.1 (#2532)
by Kentaro
34:08 queued 17:05
created

getClassNamesFromTokens()   C

Complexity

Conditions 7
Paths 6

Size

Total Lines 32
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
cc 7
eloc 25
nc 6
nop 1
dl 0
loc 32
ccs 0
cts 23
cp 0
crap 56
rs 6.7272
c 0
b 0
f 0
1
<?php
2
/*
3
 * This file is part of EC-CUBE
4
 *
5
 * Copyright(c) 2000-2017 LOCKON CO.,LTD. All Rights Reserved.
6
 *
7
 * http://www.lockon.co.jp/
8
 *
9
 * This program is free software; you can redistribute it and/or
10
 * modify it under the terms of the GNU General Public License
11
 * as published by the Free Software Foundation; either version 2
12
 * of the License, or (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
 */
23
24
namespace Eccube\Doctrine\ORM\Mapping\Driver;
25
26
27
28
use Doctrine\ORM\Mapping\MappingException;
29
use Eccube\Util\Str;
30
use PhpCsFixer\Tokenizer\Token;
31
use PhpCsFixer\Tokenizer\Tokens;
32
33
/**
34
 * 同じプロセス内で新しく生成されたProxyクラスからマッピングメタデータを抽出するためのAnnotationDriver.
35
 *
36
 * 同じプロセス内で、Proxy元のEntityがロードされた後に同じFQCNを持つProxyをロードしようとすると、Fatalエラーが発生する.
37
 * このエラーを回避するために、新しく生成されたProxyクラスは一時的にクラス名を変更してからロードして、マッピングメタデータを抽出する.
38
 */
39
class ReloadSafeAnnotationDriver extends AnnotationDriver
40
{
41
    /**
42
     * @var array 新しく生成されたProxyファイルのリスト
43
     */
44
    protected $newProxyFiles;
45
46
    protected $outputDir;
47
48
    public function setNewProxyFiles($newProxyFiles)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
49
    {
50
        $this->newProxyFiles = $newProxyFiles;
51
    }
52
53
    /**
54
     * @param string $outputDir
55
     */
56
    public function setOutputDir($outputDir)
57
    {
58
        $this->outputDir = $outputDir;
59
    }
60
61
    /**
62
     * {@inheritDoc}
63
     */
64
    public function getAllClassNames()
65
    {
66
        if ($this->classNames !== null) {
67
            return $this->classNames;
68
        }
69
70
        if (!$this->paths) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->paths of type array 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...
71
            throw MappingException::pathRequired();
72
        }
73
74
        foreach ($this->paths as $path) {
75
            if ( ! is_dir($path)) {
76
                throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
77
            }
78
79
            $iterator = new \RegexIterator(
80
                new \RecursiveIteratorIterator(
81
                    new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS),
82
                    \RecursiveIteratorIterator::LEAVES_ONLY
83
                ),
84
                '/^.+' . preg_quote($this->fileExtension) . '$/i',
0 ignored issues
show
Coding Style introduced by
Concat operator must not be surrounded by spaces
Loading history...
85
                \RecursiveRegexIterator::GET_MATCH
86
            );
87
88
            foreach ($iterator as $file) {
89
                $sourceFile = $file[0];
90
91
                if ( ! preg_match('(^phar:)i', $sourceFile)) {
92
                    $sourceFile = realpath($sourceFile);
93
                }
94
95 View Code Duplication
                foreach ($this->excludePaths as $excludePath) {
96
                    $exclude = str_replace('\\', '/', realpath($excludePath));
97
                    $current = str_replace('\\', '/', $sourceFile);
98
99
                    if (strpos($current, $exclude) !== false) {
100
                        continue 2;
101
                    }
102
                }
103
104
                $proxyFile = str_replace($path, $this->trait_proxies_directory, $sourceFile);
105
                if (file_exists($proxyFile)) {
106
                    $sourceFile = $proxyFile;
107
                }
108
109
                $this->classNames = array_merge($this->classNames ?: [], $this->getClassNamesFromTokens($sourceFile));
110
            }
111
        }
112
113
        return $this->classNames;
114
    }
115
116
    /**
117
     * ソースコードを字句解析してクラス名を解決します.
118
     * 新しく生成されたProxyクラスの場合は、一時的にクラス名を変更したクラスを生成してロードします.
119
     *
120
     * @param $sourceFile string ソースファイル
121
     * @return array ソースファイルに含まれるクラス名のリスト
122
     */
123
    private function getClassNamesFromTokens($sourceFile)
124
    {
125
        $tokens = Tokens::fromCode(file_get_contents($sourceFile));
126
        $results = [];
127
        $currentIndex = 0;
128
        while ($currentIndex = $tokens->getNextTokenOfKind($currentIndex, [[T_CLASS]])) {
129
            $classNameTokenIndex = $tokens->getNextMeaningfulToken($currentIndex);
130
            if ($classNameTokenIndex) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $classNameTokenIndex of type null|integer is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
131
                $namespaceIndex = $tokens->getNextTokenOfKind(0, [[T_NAMESPACE]]);
132
                if ($namespaceIndex) {
133
                    $namespaceEndIndex = $tokens->getNextTokenOfKind($namespaceIndex, [';']);
134
                    $namespace = $tokens->generatePartialCode($tokens->getNextMeaningfulToken($namespaceIndex), $tokens->getPrevMeaningfulToken($namespaceEndIndex));
135
                    $className = $tokens[$classNameTokenIndex]->getContent();
136
                    $fqcn = $namespace . '\\' . $className;
0 ignored issues
show
Coding Style introduced by
Concat operator must not be surrounded by spaces
Loading history...
137
                    if (class_exists($fqcn) && ! $this->isTransient($fqcn)) {
138
                        if (in_array($sourceFile, $this->newProxyFiles)) {
139
                            $newClassName = $className . Str::random(12);
0 ignored issues
show
Coding Style introduced by
Concat operator must not be surrounded by spaces
Loading history...
140
                            $tokens[$classNameTokenIndex] = new Token([T_STRING, $newClassName]);
141
                            $newFilePath = $this->outputDir."${newClassName}.php";
142
                            file_put_contents($newFilePath, $tokens->generateCode());
143
                            require_once $newFilePath;
144
                            $results[] = $namespace . "\\${newClassName}";
0 ignored issues
show
Coding Style introduced by
Concat operator must not be surrounded by spaces
Loading history...
145
                        } else {
146
                            $results[] = $fqcn;
147
                        }
148
                    }
149
                }
150
            }
151
            $currentIndex++;
152
        }
153
        return $results;
0 ignored issues
show
introduced by
Missing blank line before return statement
Loading history...
154
    }
155
}