ProxyGeneratorService::generateFiles()   C
last analyzed

Complexity

Conditions 7
Paths 33

Size

Total Lines 34
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 7

Importance

Changes 0
Metric Value
dl 0
loc 34
ccs 25
cts 25
cp 1
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 25
nc 33
nop 1
crap 7
1
<?php
2
/**
3
 * User: delboy1978uk
4
 * Date: 27/11/2016
5
 * Time: 15:48
6
 */
7
8
namespace Del\ProxyGenerator\Service;
9
10
use InvalidArgumentException;
11
use League\Flysystem\Filesystem;
12
use League\Flysystem\Adapter\Local;
13
14
class ProxyGeneratorService
15
{
16
    const REGEX_CLASS = '#class\s(?<class>\w+)(\sextends\s(?<parentClass>\w+))?(\simplements\s(?<interface>\w+))?#';
17
    const REGEX_NAMESPACE = '#namespace\s(?<namespace>\w+.*)\;#';
18
    const PROCESS_INTERFACES = 'implements';
19
    const PROCESS_CHILD_CLASSES = 'extends';
20
21
    const TEMPLATE = <<<HERE
22
<?php
23
24
namespace REPLACE_NAMESPACE;
25
26
REPLACE_USE
27
28
REPLACE_ABSTRACTclass REPLACE_CLASS REPLACE_EXTENDS REPLACE_IMPLEMENTS
29
{
30
}
31
HERE;
32
33
34
    /** @var  Filesystem */
35
    private $fileSystem;
36
37
    /** @var string $scanPath  */
38
    private $scanPath;
39
40
    /** @var string $scanPath  */
41
    private $scanInterface;
42
43
    /** @var string $replaceInterface */
44
    private $replaceInterface;
45
46
    /** @var string $targetPath */
47
    private $targetPath;
48
49
    /** @var string $targetNamespace */
50
    private $targetNamespace;
51
52
    /** @var  $replaceNamespace */
53
    private $replaceNamespace;
54
55
    /** @var array $implementingClasses */
56
    private $implementingClasses = [];
57
58
    /** @var array $extendingClasses */
59
    private $extendingClasses = [];
60
61
    private $type = self::PROCESS_INTERFACES;
62
63
    /** @var array $currentFile */
64
    private $currentFile;
65
66
    /** @var string $currentNamespace */
67
    private $currentNamespace;
68
69
    /** @var string $checkClass  */
70
    private $checkClass;
71
72
    private $newMatches = false;
73
74
    private $baseNamespace;
75
76 2
    public function __construct($projectRootPath = '.')
77
    {
78 2
        $adapter = new Local($projectRootPath);
79 2
        $this->fileSystem = new Filesystem($adapter);
80 2
        $this->targetPath = 'src';
81 2
    }
82
83
    /**
84
     * @param string $scanPath
85
     */
86 1
    public function setScanPath($scanPath)
87
    {
88 1
        $this->scanPath = $scanPath;
89 1
    }
90
91
    /**
92
     * @param string $scanInterface
93
     */
94 1
    public function setScanInterface($scanInterface)
95
    {
96 1
        $this->scanInterface = $scanInterface;
97 1
    }
98
99
    /**
100
     * @param string $replaceInterface
101
     */
102 1
    public function setReplaceInterface($replaceInterface)
103
    {
104 1
        $this->replaceInterface = $replaceInterface;
105 1
    }
106
107
    /**
108
     * @param string $targetPath
109
     * @return ProxyGeneratorService
110
     */
111 1
    public function setTargetPath($targetPath)
112
    {
113 1
        $this->targetPath = $targetPath;
114 1
        return $this;
115
    }
116
117
    /**
118
     * @param string $targetNamespace
119
     * @return ProxyGeneratorService
120
     */
121 1
    public function setTargetNamespace($targetNamespace)
122
    {
123 1
        $this->targetNamespace = $targetNamespace;
124 1
        return $this;
125
    }
126
127
    /**
128
     * @param mixed $replaceNamespace
129
     * @return ProxyGeneratorService
130
     */
131 1
    public function setReplaceNamespace($replaceNamespace)
132
    {
133 1
        $this->replaceNamespace = $replaceNamespace;
134 1
        return $this;
135
    }
136
137
138
139
    /**
140
     * @return array
141
     */
142 2
    public function generate()
143
    {
144 2
        if(empty($this->scanPath) || empty($this->scanInterface) || empty($this->replaceInterface) || empty($this->targetNamespace) || empty($this->replaceNamespace)) {
145 1
            throw new InvalidArgumentException('set scanPath, scanInterface, replaceInterface, targetNamespace and $replaceNamespace');
146
        }
147 1
        $ex = explode('\\', $this->scanInterface);
148 1
        $this->scanInterface = array_pop($ex);
149 1
        $this->baseNamespace = implode('\\', $ex);
150 1
        $classes = $this->getClasses();
151 1
        $classes = $this->generateFiles($classes);
152 1
        return $classes;
153
    }
154
155
    /**
156
     * @param $interface
157
     * @return array
158
     */
159 1
    private function getClasses()
160
    {
161
        // Get all the files to scan
162 1
        $contents = $this->fileSystem->listContents($this->scanPath, true);
163 1
        $classes = $this->processContents($contents);
164 1
        return $classes;
165
    }
166
167
    /**
168
     * @param $contents
169
     * @param $interfaceName
170
     * @return array
171
     */
172 1
    private function processContents($contents)
173
    {
174
        // Find all classes implementing $interfaceName
175 1
        $this->type = self::PROCESS_INTERFACES;
176 1
        foreach ($contents as $file) {
177 1
            $this->processFile($file);
178
        }
179 1
        $this->type = self::PROCESS_CHILD_CLASSES;
180 1
        $this->processClasses($this->implementingClasses, $contents);
181
        do {
182 1
            $this->newMatches = false;
183 1
            $this->processClasses($this->extendingClasses, $contents);
184 1
            $keepGoing = $this->newMatches;
185 1
        } while ($keepGoing === true);
186
187 1
        return array_merge($this->implementingClasses, $this->extendingClasses);
188
    }
189
190 1
    private function processClasses($classes, $contents)
191
    {
192 1
        foreach ($classes as $class) {
193 1
            $this->checkClass = $class['class'];
194 1
            foreach($contents as $file) {
195 1
                $this->processFile($file);
196
            }
197
        }
198 1
    }
199
200
    /**
201
     * @param $file
202
     * @return bool
203
     */
204 1
    private function processFile($file)
205
    {
206 1
        $this->currentFile = $file;
207 1
        if (!isset($file['extension']) || $file['extension'] != 'php') {
208 1
            return false;
209
        }
210 1
        $filePath = $file['path'];
211 1
        $contents = $this->fileSystem->read($filePath);
212 1
        foreach (explode("\n", $contents) as $line) {
213 1
            $this->processLine($line);
214
        }
215 1
        return true;
216
    }
217
218 1
    private function processLine($line)
219
    {
220 1
        preg_match(self::REGEX_NAMESPACE, $line, $match);
221 1
        if (!empty($match['namespace'])) {
222 1
            $this->currentNamespace = $match['namespace'];
223
        }
224 1
        preg_match(self::REGEX_CLASS, $line, $match);
225 1
        if (!empty($match)) {
226 1
            $match['abstract'] = (strstr($line, 'abstract ')) ? true : false;
227 1
            $this->processMatch($match);
228
        }
229 1
    }
230
231 1
    private function processMatch(array $match)
232
    {
233 1
        switch ($this->type) {
234 1
            case self::PROCESS_CHILD_CLASSES:
235 1
                $this->processExtendingClass($match);
236 1
                break;
237 1
            case self::PROCESS_INTERFACES:
238
            default:
239 1
                $this->processImplementingClass($match);
240 1
                break;
241
        }
242 1
    }
243
244 1
    private function processImplementingClass(array $match)
245
    {
246 1
        if(!empty($match['interface']) && $match['interface'] == $this->scanInterface) {
247 1
            $this->currentFile['interface'] = $match['interface'];
248 1
            $this->currentFile['abstract'] = $match['abstract'];
249 1
            $this->currentFile['class'] = $match['class'];
250 1
            $this->currentFile['parentClass'] = isset($match['parentClass']) ? $match['parentClass'] : null;
251 1
            $this->currentFile['namespace'] = $this->currentNamespace;
252 1
            if(!in_array($this->implementingClasses, $this->currentFile)) {
253 1
                $this->implementingClasses[] = $this->currentFile;
254
            }
255
        }
256 1
    }
257
258 1
    private function processExtendingClass(array $match)
259
    {
260 1
        if(isset($match['parentClass']) && $this->checkClass == $match['parentClass']) {
261 1
            $this->currentFile['abstract'] = $match['abstract'];
262 1
            $this->currentFile['class'] = $match['class'];
263 1
            $this->currentFile['parentClass'] = $match['parentClass'];
264 1
            $this->currentFile['namespace'] = $this->currentNamespace;
265 1
            $key = $this->currentNamespace.'\\'.$this->currentFile['class'];
266 1
            if(!isset($this->extendingClasses[$key])) {
267 1
                $this->extendingClasses[$key] = $this->currentFile;
268 1
                $this->newMatches = true;
269
            }
270
        }
271 1
    }
272
273
274 1
    private function generateFiles($classes)
275
    {
276 1
        $newClasses = [];
277 1
        foreach ($classes as $class) {
278
279 1
            $file = self::TEMPLATE;
280 1
            $namespace = isset($class['namespace']) ? $class['namespace'] : null;
281 1
            $extraNamespace = str_replace($this->targetNamespace, '', $namespace);
282 1
            $alias = 'ThirdParty'.$class['class'];
283 1
            $use = 'use '.$class['namespace'].'\\'.$class['class'].' as '.$alias.';';
284 1
            $abstract = $class['abstract'] ? 'abstract ' : null;
285 1
            $extends = 'extends '.$alias;
286
287 1
            $use .= ($extraNamespace != '') ? "\n".'use '.$this->replaceInterface.';' : null;
288 1
            $interface = str_replace($this->replaceNamespace.'\\', '',$this->replaceInterface);
289 1
            $implements = 'implements ' . $interface;
290
291
292 1
            $file = str_replace('REPLACE_NAMESPACE', $this->replaceNamespace.$extraNamespace, $file);
293 1
            $file = str_replace('REPLACE_USE', $use, $file);
294 1
            $file = str_replace('REPLACE_ABSTRACT', $abstract, $file);
295 1
            $file = str_replace('REPLACE_CLASS', $class['class'], $file);
296 1
            $file = str_replace('REPLACE_EXTENDS', $extends, $file);
297 1
            $file = str_replace('REPLACE_IMPLEMENTS', $implements, $file);
298
299 1
            $extraPath = !empty($extraNamespace) ? str_replace('\\', '', $extraNamespace).DIRECTORY_SEPARATOR : null;
300 1
            $location = $this->targetPath.DIRECTORY_SEPARATOR.$extraPath.$class['class'].'.php';
301 1
            if(!$this->fileSystem->has($location)) {
302 1
                $this->fileSystem->write($location, $file);
303
            }
304 1
            $newClasses[] = $location;
305
        }
306 1
        return $newClasses;
307
    }
308
}