Failed Conditions
Push — experimental/3.1 ( c6bbe9...5ca513 )
by Yangsin
235:43 queued 228:52
created

EntityProxyService::generate()   B

Complexity

Conditions 9
Paths 82

Size

Total Lines 54
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 31
CRAP Score 9

Importance

Changes 0
Metric Value
cc 9
eloc 31
nc 82
nop 4
dl 0
loc 54
ccs 31
cts 31
cp 1
crap 9
rs 7.255
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Service;
25
26
27
use Doctrine\Common\Annotations\AnnotationReader;
28
use Doctrine\ORM\EntityManager;
29
use Eccube\Annotation\EntityExtension;
30
use Eccube\Annotation\Inject;
31
use Eccube\Annotation\Service;
32
use Symfony\Component\Console\Output\ConsoleOutput;
33
use Symfony\Component\Console\Output\OutputInterface;
34
use Symfony\Component\Finder\Finder;
35
use Zend\Code\Generator\ClassGenerator;
36
use Zend\Code\Generator\FileGenerator;
37
use Zend\Code\Reflection\ClassReflection;
38
39
/**
40
 * @Service
41
 */
42
class EntityProxyService
43
{
44
    /**
45
     * @Inject("orm.em")
46
     * @var EntityManager
47
     */
48
    protected $entityManager;
49
50
    /**
51
     * EntityのProxyを生成します。
52
     *
53
     * @param array $includesDirs Proxyに含めるTraitがあるディレクトリ一覧
0 ignored issues
show
introduced by
Expected 11 spaces after parameter type; 1 found
Loading history...
54
     * @param array $excludeDirs Proxyから除外するTraitがあるディレクトリ一覧
0 ignored issues
show
introduced by
Expected 11 spaces after parameter type; 1 found
Loading history...
introduced by
Expected 2 spaces after parameter name; 1 found
Loading history...
55
     * @param string $outputDir 出力先
0 ignored issues
show
introduced by
Expected 10 spaces after parameter type; 1 found
Loading history...
introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
56
     * @param OutputInterface $output ログ出力
0 ignored issues
show
introduced by
Expected 7 spaces after parameter name; 1 found
Loading history...
57
     * @return array 生成したファイルのリスト
58
     */
59 14
    public function generate($includesDirs, $excludeDirs, $outputDir, OutputInterface $output = null)
60
    {
61 14
        if (is_null($output)) {
62 14
            $output = new ConsoleOutput();
63
        }
64
65 14
        $generatedFiles = [];
66
67 14
        list($addTraits, $removeTrails) = $this->scanTraits([$includesDirs, $excludeDirs]);
68 14
        $targetEntities = array_unique(array_merge(array_keys($addTraits), array_keys($removeTrails)));
69
70
        // プロキシファイルの生成
71 14
        foreach ($targetEntities as $targetEntity) {
72 14
            $traits = isset($addTraits[$targetEntity]) ? $addTraits[$targetEntity] : [];
73 14
            $rc = new ClassReflection($targetEntity);
74 14
            $generator = ClassGenerator::fromReflection($rc);
75 14
            $uses = FileGenerator::fromReflectedFileName($rc->getFileName())->getUses();
76
77 14
            foreach ($uses as $use) {
78 14
                $generator->addUse($use[0], $use[1]);
79
            }
80
81 14
            if (isset($removeTrails[$targetEntity])) {
82 4
                foreach ($removeTrails[$targetEntity] as $trait) {
83 4
                    $this->removePropertiesFromProxy($trait, $generator);
84
                }
85
            }
86
87 14
            foreach ($traits as $trait) {
88 14
                $this->removePropertiesFromProxy($trait, $generator);
89 14
                $generator->addTrait('\\' . $trait);
0 ignored issues
show
Coding Style introduced by
Concat operator must not be surrounded by spaces
Loading history...
90
            }
91
92
            // extendしたクラスが相対パスになるので
93 14
            $extendClass = $generator->getExtendedClass();
94 14
            $generator->setExtendedClass('\\'.$extendClass);
95
96
            // interfaceが相対パスになるので
97 14
            $interfaces = $generator->getImplementedInterfaces();
98 14
            foreach ($interfaces as &$interface) {
99 12
                $interface = '\\'.$interface;
100
            }
101 14
            $generator->setImplementedInterfaces($interfaces);
102
103 14
            $file = basename($rc->getFileName());
104
105 14
            $code = $generator->generate();
106 14
            $generatedFiles[] = $outputFile = $outputDir.'/'.$file;
107 14
            file_put_contents($outputFile, '<?php '.PHP_EOL.$code);
108 14
            $output->writeln('gen -> '.$outputFile);
109
        }
110
111 14
        return $generatedFiles;
112
    }
113
114
    /**
115
     * 複数のディレクトリセットをスキャンしてディレクトリセットごとのEntityとTraitのマッピングを返します.
116
     * @param $dirSets array スキャン対象ディレクトリリストの配列
117
     * @return array ディレクトリセットごとのEntityとTraitのマッピング
118
     */
119 14
    private function scanTraits($dirSets)
120
    {
121
        // ディレクトリセットごとのファイルをロードしつつ一覧を作成
122 14
        $includedFileSets = [];
123 14
        foreach ($dirSets as $dirSet) {
124 14
            $includedFiles = [];
125 14
            $dirs = array_filter($dirSet, 'file_exists');
126 14
            if (!empty($dirs)) {
127 14
                $files = Finder::create()
128 14
                    ->in($dirs)
129 14
                    ->name('*.php')
130 14
                    ->files();
131
132 14
                foreach ($files as $file) {
133 14
                    require_once $file->getRealPath();
134 14
                    $includedFiles[] = $file->getRealPath();
135
                }
136
            }
137 14
            $includedFileSets[] = $includedFiles;
138
        }
139
140 14
        $declaredTraits = get_declared_traits();
141
142
        // ディレクトリセットに含まれるTraitの一覧を作成
143
        $traitSets = array_map(function() { return []; }, $dirSets);
0 ignored issues
show
Coding Style introduced by
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
Coding Style introduced by
Opening brace must be the last content on the line
Loading history...
Coding Style introduced by
It is generally recommended to place each PHP statement on a line by itself.

Let’s take a look at an example:

// Bad
$a = 5; $b = 6; $c = 7;

// Good
$a = 5;
$b = 6;
$c = 7;
Loading history...
144 14
        foreach ($declaredTraits as $className) {
145 14
            $rc = new \ReflectionClass($className);
146 14
            $sourceFile = $rc->getFileName();
147 14
            foreach ($includedFileSets as $index=>$includedFiles) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space before "=>"; 0 found
Loading history...
Coding Style introduced by
Expected 1 space after "=>"; 0 found
Loading history...
148 14
                if (in_array($sourceFile, $includedFiles)) {
149 14
                    $traitSets[$index][] = $className;
150
                }
151
            }
152
        }
153
154
        // TraitをEntityごとにまとめる
155 14
        $reader = new AnnotationReader();
156 14
        $proxySets = [];
157 14
        foreach ($traitSets as $traits) {
158 14
            $proxies = [];
159 14
            foreach ($traits as $trait) {
160 14
                $anno = $reader->getClassAnnotation(new \ReflectionClass($trait), EntityExtension::class);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $anno is correct as $reader->getClassAnnotat...EntityExtension::class) (which targets Doctrine\Common\Annotati...r::getClassAnnotation()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
161 14
                if ($anno) {
162 14
                    $proxies[$anno->value][] = $trait;
163
                }
164
            }
165 14
            $proxySets[] = $proxies;
166
        }
167
168 14
        return $proxySets;
169
    }
170
171
    /**
172
     * @param $trait
173
     * @param $generator
174
     */
175 14
    private function removePropertiesFromProxy($trait, $generator)
176
    {
177 14
        $rt = new ClassReflection($trait);
178 14
        foreach ($rt->getProperties() as $prop) {
179
            // すでにProxyがある場合, $generatorにuse XxxTrait;が存在せず,
180
            // traitに定義されているフィールド,メソッドがクラス側に追加されてしまう
181 14
            if ($generator->hasProperty($prop->getName())) {
182
                // $generator->removeProperty()はzend-code 2.6.3 では未実装なのでリフレクションで削除.
183
                $generatorRefObj = new \ReflectionObject($generator);
184
                $generatorRefProp = $generatorRefObj->getProperty('properties');
185
                $generatorRefProp->setAccessible(true);
186
                $properies = $generatorRefProp->getValue($generator);
187
                unset($properies[$prop->getName()]);
188 14
                $generatorRefProp->setValue($generator, $properies);
189
            }
190
        }
191 14
        foreach ($rt->getMethods() as $method) {
192
            if ($generator->hasMethod($method->getName())) {
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
193
                $generator->removeMethod($method->getName());
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
194
            }
195
        }
196
    }
197
}
198