StructureMap   F
last analyzed

Complexity

Total Complexity 90

Size/Duplication

Total Lines 771
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 90
lcom 1
cbo 4
dl 0
loc 771
rs 1.829
c 0
b 0
f 0

23 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 31 3
A getEnforcedFiles() 0 19 2
B getEntries() 0 29 6
A add() 0 15 1
A isEmpty() 0 4 1
A entryExists() 0 4 1
A fill() 0 11 2
A update() 0 4 1
A getEntry() 0 21 3
B isRecent() 0 24 6
A getIdentifiers() 0 18 4
A getFiles() 0 18 3
A remove() 0 12 2
B reIndex() 0 40 6
A findVersion() 0 14 3
A getProjectIterator() 0 12 2
A getDirectoryIterator() 0 27 3
B generate() 0 70 5
A isFileEnforced() 0 18 5
A findAnnotations() 0 26 4
D findIdentifier() 0 84 23
A load() 0 16 2
A save() 0 19 2

How to fix   Complexity   

Complex Class

Complex classes like StructureMap often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use StructureMap, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * \AppserverIo\Doppelgaenger\StructureMap
5
 *
6
 * NOTICE OF LICENSE
7
 *
8
 * This source file is subject to the Open Software License (OSL 3.0)
9
 * that is available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * PHP version 5
13
 *
14
 * @author    Bernhard Wick <[email protected]>
15
 * @copyright 2015 TechDivision GmbH - <[email protected]>
16
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
17
 * @link      https://github.com/appserver-io/doppelgaenger
18
 * @link      http://www.appserver.io/
19
 */
20
21
namespace AppserverIo\Doppelgaenger;
22
23
use AppserverIo\Doppelgaenger\Entities\Definitions\Structure;
24
use AppserverIo\Doppelgaenger\Exceptions\ParserException;
25
use AppserverIo\Doppelgaenger\Interfaces\MapInterface;
26
use AppserverIo\Doppelgaenger\Utils\Formatting;
27
use AppserverIo\Psr\MetaobjectProtocol\Aop\Annotations\Advices\After;
28
use AppserverIo\Psr\MetaobjectProtocol\Aop\Annotations\Advices\AfterReturning;
29
use AppserverIo\Psr\MetaobjectProtocol\Aop\Annotations\Advices\AfterThrowing;
30
use AppserverIo\Psr\MetaobjectProtocol\Aop\Annotations\Advices\Around;
31
use AppserverIo\Psr\MetaobjectProtocol\Aop\Annotations\Advices\Before;
32
use AppserverIo\Psr\MetaobjectProtocol\Aop\Annotations\Aspect;
33
use AppserverIo\Psr\MetaobjectProtocol\Aop\Annotations\Introduce;
34
use AppserverIo\Psr\MetaobjectProtocol\Aop\Annotations\Pointcut;
35
use AppserverIo\Psr\MetaobjectProtocol\Dbc\Annotations\Ensures;
36
use AppserverIo\Psr\MetaobjectProtocol\Dbc\Annotations\Invariant;
37
use AppserverIo\Psr\MetaobjectProtocol\Dbc\Annotations\Requires;
38
39
/**
40
 * This class provides the possibility to hold a map of structure entries, which are used to relate a structure
41
 * definition to it's physical path and other meta information
42
 *
43
 * @author    Bernhard Wick <[email protected]>
44
 * @copyright 2015 TechDivision GmbH - <[email protected]>
45
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
46
 * @link      https://github.com/appserver-io/doppelgaenger
47
 * @link      http://www.appserver.io/
48
 */
49
class StructureMap implements MapInterface
50
{
51
    /**
52
     * @var array $map The actual container for the map
53
     */
54
    protected $map;
55
56
    /**
57
     * @var array $rootPaths The $autoloaderPaths and $enforcementPaths combined to build up paths we have to index
58
     */
59
    protected $rootPaths;
60
61
    /**
62
     * @var array $enforcementPaths Which paths do we like to include in our map?
63
     */
64
    protected $autoloaderPaths;
65
66
    /**
67
     * @var array $enforcementPaths Which paths do we have to enforce
68
     */
69
    protected $enforcementPaths;
70
71
    /**
72
     * @var string $mapPath Where will the map be stored?
73
     */
74
    protected $mapPath;
75
76
    /**
77
     * @var \AppserverIo\Doppelgaenger\Config $config Configuration
78
     */
79
    protected $config;
80
81
    /**
82
     * @var \Iterator|null $projectIterator Will hold the iterator over the project root pathes if needed
83
     */
84
    protected $projectIterator;
85
86
    /**
87
     * @var string $version Will hold the version of the currently loaded map
88
     */
89
    protected $version;
90
91
    /**
92
     * Default constructor
93
     *
94
     * @param array                             $autoloaderPaths  Which paths do we like to include in our map?
95
     * @param array                             $enforcementPaths Which paths do we have to enforce
96
     * @param \AppserverIo\Doppelgaenger\Config $config           Configuration
97
     */
98
    public function __construct($autoloaderPaths, $enforcementPaths, Config $config)
99
    {
100
        // Init as empty map
101
        $this->map = array();
102
103
        // As we do accept arrays we have to be sure that we got one. If not we convert it.
104
        if (!is_array($enforcementPaths)) {
105
            $enforcementPaths = array($enforcementPaths);
106
        }
107
        if (!is_array($autoloaderPaths)) {
108
            $autoloaderPaths = array($autoloaderPaths);
109
        }
110
111
        // Save the config for later use.
112
        $this->config = $config;
113
114
        // Set the enforcementPaths and autoloaderPaths and calculate the path to the map file
115
        $this->enforcementPaths = $enforcementPaths;
116
        $this->autoloaderPaths = $autoloaderPaths;
117
118
        // The rootPaths member holds the other path members combined to build up the root paths we have to create
119
        // an index for
120
        $this->rootPaths = array_merge($autoloaderPaths, $enforcementPaths);
121
122
        // Build up the path of the serialized map.
123
        // Get ourselves a format utility
124
        $formattingUtil = new Formatting();
125
        $this->mapPath = $formattingUtil->sanitizeSeparators($this->config->getValue('cache/dir') . DIRECTORY_SEPARATOR . md5(
126
            implode('', $autoloaderPaths) . implode('', $enforcementPaths)
127
        ));
128
    }
129
130
    /**
131
     * Will return a list of files which are within the enforcementPaths
132
     *
133
     * @return array
134
     */
135
    protected function getEnforcedFiles()
136
    {
137
        // get an iterator over all files we have to enforce so we can compare the files
138
        $recursiveEnforcementIterator = $this->getDirectoryIterator($this->enforcementPaths);
139
        $regexEnforcementIterator = new \RegexIterator(
140
            $recursiveEnforcementIterator,
141
            '/^.+\.php$/i',
142
            \RecursiveRegexIterator::GET_MATCH
143
        );
144
145
        // iterator over our enforcement iterators and build up an array for later checks
146
        $enforcedFiles = array();
147
        foreach ($regexEnforcementIterator as $file) {
148
            // collect what we got in a way we can search it fast
149
            $enforcedFiles[$file[0]] = '';
150
        }
151
152
        return $enforcedFiles;
153
    }
154
155
    /**
156
     * Will return all entries within a map. If needed only entries of contracted
157
     * structures will be returned.
158
     *
159
     * @param boolean $annotated Do we only want entries containing useful annotations?
160
     * @param boolean $enforced  Do we only want entries which are enforced?
161
     *
162
     * @return mixed
163
     */
164
    public function getEntries($annotated = false, $enforced = false)
165
    {
166
        // Our structures
167
        $structures = array();
168
169
        foreach ($this->map as $entry) {
170
            // If we only need contracted only
171
            if (($annotated === true && $entry['hasAnnotations'] === false)) {
172
                continue;
173
            }
174
175
            // If we only need enforced only
176
            if (($enforced === true && $entry['enforced'] === false)) {
177
                continue;
178
            }
179
180
            $structures[] = new Structure(
181
                $entry['cTime'],
182
                $entry['identifier'],
183
                $entry['path'],
184
                $entry['type'],
185
                $entry['hasAnnotations'],
186
                $entry['enforced']
187
            );
188
        }
189
190
        // Return the structure DTOs
191
        return $structures;
192
    }
193
194
    /**
195
     * Will add a structure entry to the map.
196
     *
197
     * @param \AppserverIo\Doppelgaenger\Entities\Definitions\Structure $structure The structure to add
198
     *
199
     * @return bool
200
     */
201
    public function add(Structure $structure)
202
    {
203
        // The the entry
204
        $this->map[$structure->getIdentifier()] = array(
205
            'cTime' => $structure->getCTime(),
206
            'identifier' => $structure->getIdentifier(),
207
            'path' => $structure->getPath(),
208
            'type' => $structure->getType(),
209
            'hasAnnotations' => $structure->hasAnnotations(),
210
            'enforced' => $structure->isEnforced()
211
        );
212
213
        // Persist the map
214
        return $this->save();
215
    }
216
217
    /**
218
     * Will return true if there is no map generated by now. False if there are map entries
219
     *
220
     * @return boolean
221
     */
222
    public function isEmpty()
223
    {
224
        return empty($this->map);
225
    }
226
227
    /**
228
     * Do we have an entry for the given identifier
229
     *
230
     * @param string $identifier The identifier of the entry we try to find
231
     *
232
     * @return bool
233
     */
234
    public function entryExists($identifier)
235
    {
236
        return isset($this->map[$identifier]);
237
    }
238
239
    /**
240
     * Will fill the structure map according to it's config
241
     *
242
     * @return boolean
243
     */
244
    public function fill()
245
    {
246
        // Load the serialized map.
247
        // If there is none or it isn't current we will generate it anew.
248
        if (!$this->load()) {
249
            $this->generate();
250
        }
251
252
        // Still here? Sounds good
253
        return true;
254
    }
255
256
    /**
257
     * Will update a given structure.
258
     * If the entry does not exist we will create it
259
     *
260
     * @param \AppserverIo\Doppelgaenger\Entities\Definitions\Structure|null $structure The structure to update
261
     *
262
     * @return void
263
     *
264
     * TODO implement this in the implementing classes
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
265
     */
266
    public function update(Structure $structure = null)
267
    {
268
269
    }
270
271
    /**
272
     * Will return the entry specified by it's identifier.
273
     * If none is found, false will be returned.
274
     *
275
     * @param string $identifier The identifier of the entry we try to find
276
     *
277
     * @return boolean|\AppserverIo\Doppelgaenger\Entities\Definitions\Structure
278
     */
279
    public function getEntry($identifier)
280
    {
281
        if (is_string($identifier) && isset($this->map[$identifier])) {
282
            // We got it, lets build a structure object
283
            $entry = $this->map[$identifier];
284
            $structure = new Structure(
285
                $entry['cTime'],
286
                $entry['identifier'],
287
                $entry['path'],
288
                $entry['type'],
289
                $entry['hasAnnotations'],
290
                $entry['enforced']
291
            );
292
293
            // Return the structure DTO
294
            return $structure;
295
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
296
        } else {
297
            return false;
298
        }
299
    }
300
301
    /**
302
     * Checks if the entry for a certain structure is recent if one was specified.
303
     * If not it will check if the whole map is recent.
304
     *
305
     * @param null|string $identifier The identifier of the entry we try to find
306
     *
307
     * @return  boolean
308
     */
309
    public function isRecent($identifier = null)
310
    {
311
        // Our result
312
        $result = false;
313
314
        // If we got a class name
315
        if ($identifier !== null && isset($this->map[$identifier])) {
316
            // Is the stored file time the same as directly from the file?
317
            if ($this->map[$identifier]['cTime'] === filectime($this->map[$identifier]['path'])) {
318
                return true;
319
            }
320
        }
321
322
        // We got no class name, check the whole thing
323
        if ($identifier === null) {
324
            // Is the saved version the same as of the current file system?
325
            if ($this->version === $this->findVersion()) {
326
                return true;
327
            }
328
        }
329
330
        // Still here? That seems wrong.
331
        return $result;
332
    }
333
334
    /**
335
     * Will return an array of all entry identifiers which are stored in this map.
336
     * We might filter by entry type
337
     *
338
     * @param string|null $type The type to filter by
339
     *
340
     * @return array
341
     */
342
    public function getIdentifiers($type = null)
343
    {
344
        if ($type === null) {
345
            return array_keys($this->map);
346
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
347
        } else {
348
            // Collect the data.
349
            $result = array();
350
            foreach ($this->map as $identifier => $file) {
351
                if ($file['type'] === $type) {
352
                    // filter by type
353
                    $result[] = $identifier;
354
                }
355
            }
356
357
            return $result;
358
        }
359
    }
360
361
    /**
362
     * Will return an array of all files which are stored in this map.
363
     * Will include the full path if $fullPath is true.
364
     *
365
     * @param boolean $fullPath Do we need the full path?
366
     *
367
     * @return  array
368
     */
369
    public function getFiles($fullPath = true)
370
    {
371
        // What information do we have to get?
372
        if ($fullPath === true) {
373
            $method = 'realpath';
374
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
375
        } else {
376
            $method = 'dirname';
377
        }
378
379
        // Collect the data.
380
        $result = array();
381
        foreach ($this->map as $file) {
382
            $result[] = $method($file['path']);
383
        }
384
385
        return $result;
386
    }
387
388
    /**
389
     * Removes an entry from the map of structures.
390
     *
391
     * @param null|string $identifier The identifier of the entry we try to find
392
     *
393
     * @return boolean
394
     */
395
    public function remove($identifier)
396
    {
397
        if (isset($this->map[$identifier])) {
398
            // only try to remove if found
399
            unset($this->map[$identifier]);
400
401
            return true;
402
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
403
        } else {
404
            return false;
405
        }
406
    }
407
408
    /**
409
     * Will reindex the structure map aka create it anew.
410
     * If $specificPath is supplied we will reindex the specified path only and add it to the map.
411
     * $specificPath MUST be within the root pathes of this StructureMap instance, otherwise it is no REindexing.
412
     *
413
     * @param string|null $specificPath A certain path which will be reindexed
414
     *
415
     * @return boolean
416
     */
417
    public function reIndex($specificPath = null)
418
    {
419
        // If we have no specific path we will delete the current map
420
        if ($specificPath === null) {
421
            // Make our map empty
422
            $this->map = array();
423
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
424
        } else {
425
            // We need some formatting utilities and normalize the path as it might contain regex
426
            $formattingUtil = new Formatting();
427
            $specificPath = $formattingUtil->normalizePath($specificPath);
428
429
            // If there was a specific path give, we have to check it for compatibility with $this.
430
            // First thing: is it contained in one of $this root pathes, if not it is no REindexing
431
            $isContained = false;
432
            foreach ($this->rootPaths as $rootPath) {
433
                if (strpos($specificPath, $rootPath) === 0) {
434
                    $isContained = true;
435
                    break;
436
                }
437
            }
438
439
            // Did we find it?
440
            if (!$isContained) {
441
                return false;
442
            }
443
444
            // Second thing: is the path readable?
445
            if (!is_readable($specificPath)) {
446
                return false;
447
            }
448
449
            // Everything fine, set the root path to our specific path
450
            $this->rootPaths = array($specificPath);
451
        }
452
453
        // Generate the map, all needed details have been altered above
454
        $this->generate();
455
        return true;
456
    }
457
458
    /**
459
     * Will return a "version" of the current project file base by checking the cTime of all directories
460
     * within the project root paths and create a sha1 hash over them.
461
     *
462
     * @return string
463
     */
464
    protected function findVersion()
465
    {
466
        $recursiveIteratore = $this->getProjectIterator();
467
468
        $tmp = '';
469
        foreach ($recursiveIteratore as $fileInfo) {
470
            if ($fileInfo->isDir()) {
471
                // we check directories only
472
                $tmp += $fileInfo->getCTime();
473
            }
474
        }
475
476
        return sha1($tmp);
477
    }
478
479
    /**
480
     * Will Return an iterator over our project files
481
     *
482
     * @return \Iterator
483
     */
484
    protected function getProjectIterator()
485
    {
486
        // If we already got it we can return it directly
487
        if (isset($this->projectIterator)) {
488
            return $this->projectIterator;
489
        }
490
491
        // Save our result for later reuse
492
        $this->projectIterator = $this->getDirectoryIterator($this->rootPaths);
493
494
        return $this->projectIterator;
495
    }
496
497
    /**
498
     * Will Return an iterator over a set of files determined by a list of directories to iterate over
499
     *
500
     * @param array $paths List of directories to iterate over
501
     *
502
     * @return \Iterator
503
     */
504
    protected function getDirectoryIterator(array $paths)
505
    {
506
507
        // As we might have several rootPaths we have to create several RecursiveDirectoryIterators.
508
        $directoryIterators = array();
509
        foreach ($paths as $path) {
510
            $directoryIterators[] = new \RecursiveDirectoryIterator(
511
                $path,
512
                \RecursiveDirectoryIterator::SKIP_DOTS
513
            );
514
        }
515
516
        // We got them all, now append them onto a new RecursiveIteratorIterator and return it.
517
        $recursiveIterator = new \AppendIterator();
518
        foreach ($directoryIterators as $directoryIterator) {
519
            // Append the directory iterator
520
            $recursiveIterator->append(
521
                new \RecursiveIteratorIterator(
522
                    $directoryIterator,
523
                    \RecursiveIteratorIterator::SELF_FIRST,
524
                    \RecursiveIteratorIterator::CATCH_GET_CHILD
525
                )
526
            );
527
        }
528
529
        return $recursiveIterator;
530
    }
531
532
533
    /**
534
     * Will generate the structure map within the specified root path.
535
     *
536
     * @return void
537
     */
538
    protected function generate()
539
    {
540
        // first of all we will get the version, so we will later know about changes made DURING indexing
541
        $this->version = $this->findVersion();
542
543
        // get the iterator over our project files and create a regex iterator to filter what we got
544
        $recursiveIterator = $this->getProjectIterator();
545
        $regexIterator = new \RegexIterator($recursiveIterator, '/^.+\.php$/i', \RecursiveRegexIterator::GET_MATCH);
546
547
        // get the list of enforced files
548
        $enforcedFiles= $this->getEnforcedFiles();
549
550
        // if we got namespaces which are omitted from enforcement we have to mark them as such
551
        $omittedNamespaces = array();
552
        if ($this->config->hasValue('enforcement/omit')) {
553
            $omittedNamespaces = $this->config->getValue('enforcement/omit');
554
        }
555
556
        // iterator over our project files and add array based structure representations
557
        foreach ($regexIterator as $file) {
558
            // get the identifiers if any.
559
            $identifier = $this->findIdentifier($file[0]);
560
561
            // if we got an identifier we can build up a new map entry
562
            if ($identifier !== false) {
563
                // We need to get our array of needles
564
                $needles = array(
565
                    Invariant::ANNOTATION,
566
                    Ensures::ANNOTATION,
567
                    Requires::ANNOTATION,
568
                    After::ANNOTATION,
569
                    AfterReturning::ANNOTATION,
570
                    AfterThrowing::ANNOTATION,
571
                    Around::ANNOTATION,
572
                    Before::ANNOTATION,
573
                    Introduce::ANNOTATION,
574
                    Pointcut::ANNOTATION
575
                );
576
577
                // If we have to enforce things like @param or @returns, we have to be more sensitive
578
                if ($this->config->getValue('enforcement/enforce-default-type-safety') === true) {
579
                    $needles[] = '@var';
580
                    $needles[] = '@param';
581
                    $needles[] = '@return';
582
                }
583
584
                // check if the file has contracts and if it should be enforced
585
                $hasAnnotations = $this->findAnnotations($file[0], $needles);
586
587
                // create the entry
588
                $this->map[$identifier[1]] = array(
589
                    'cTime' => filectime($file[0]),
590
                    'identifier' => $identifier[1],
591
                    'path' => $file[0],
592
                    'type' => $identifier[0],
593
                    'hasAnnotations' => $hasAnnotations,
594
                    'enforced' => $this->isFileEnforced(
595
                        $file[0],
596
                        $identifier[1],
597
                        $hasAnnotations,
598
                        $enforcedFiles,
599
                        $omittedNamespaces
600
                    )
601
                );
602
            }
603
        }
604
605
        // save for later reuse.
606
        $this->save();
607
    }
608
609
    /**
610
     * Check if the file should be enforced by considering three factors:
611
     *  1. does it have contracts?
612
     *  2. is it within the list of enforced files?
613
     *  3. is it within the namespaces omitted of enforcement?
614
     *
615
     * @param string  $file              The path of the file to be tested
616
     * @param string  $fileIdentifier    The qualified name of the file's structure
617
     * @param boolean $hasAnnotations    Does this file contain contracts (as epr current configuration)
618
     * @param array   $enforcedFiles     Array of files which need to be enforced
619
     * @param array   $omittedNamespaces Array of namespaces which are omitted from the enforcement
620
     *
621
     * @return boolean
622
     */
623
    protected function isFileEnforced($file, $fileIdentifier, $hasAnnotations, $enforcedFiles, $omittedNamespaces)
624
    {
625
        // if the file is within an omitted namespace it most certainly is not
626
        foreach ($omittedNamespaces as $omittedNamespace) {
627
            if (strpos($fileIdentifier, ltrim($omittedNamespace, '\\')) === 0) {
628
                return false;
629
            }
630
        }
631
632
        // as we are still here we are not within an omitted namespace.
633
        // if both of the below is true the file needs to be enforced
634
        if ($hasAnnotations === true && isset($enforcedFiles[$file])) {
635
            return true;
636
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
637
        } else {
638
            return false;
639
        }
640
    }
641
642
    /**
643
     * Will return true if the specified file has annotations which might be useful, false if not
644
     *
645
     * @param string $file        File to check for contracts
646
     * @param array  $annotations Array of annotations to look for
647
     *
648
     * @return boolean
649
     */
650
    protected function findAnnotations($file, array $annotations)
651
    {
652
653
        // Open the file and search it piece by piece until we find something or the file ends.
654
        $rsc = fopen($file, 'r');
655
        $recent = '';
656
        while (!feof($rsc)) {
657
            // Get a current chunk
658
            $current = fread($rsc, 512);
659
660
            // We also check the last chunk as well, to avoid cutting the only needle we have in two.
661
            $haystack = $recent . $current;
662
            foreach ($annotations as $annotation) {
663
                // If we found something we can return true
664
                if (strpos($haystack, '@' . ltrim($annotation, '@')) !== false) {
665
                    return true;
666
                }
667
            }
668
669
            // Set recent for the next iteration
670
            $recent = $current;
671
        }
672
673
        // Still here? So nothing was found.
674
        return false;
675
    }
676
677
    /**
678
     * Will get the identifier of a structure within a name.
679
     * Identifier will be most likely the qualified name including namespace and structure name.
680
     * May return false on error.
681
     * Will return an ugly array of the sort array(<STRUCTURE_TYPE>, <STRUCTURE_NAME>)
682
     *
683
     * @param string $file The file to check
684
     *
685
     * @return array|boolean
686
     * @throws \Exception|\AppserverIo\Doppelgaenger\Exceptions\ParserException
687
     */
688
    protected function findIdentifier($file)
689
    {
690
691
        $code = file_get_contents($file);
692
693
        // if we could not open the file tell them
694
        if ($code === false) {
695
            throw new ParserException(sprintf('Could not open file %s for type inspection.', $file));
696
        }
697
698
        // some variables we will need
699
        $namespace = '';
700
        $type = '';
701
        $stuctureName = '';
702
703
        // get the tokens and their count
704
        $tokens = @token_get_all($code);
705
        $count = count($tokens);
706
707
        // iterate over the current set of tokens and filter out what we found
708
        for ($i = 0; $i < $count; $i++) {
709
            if ($tokens[$i][0] == T_NAMESPACE) {
710
                // we passed the namespace token, lets collect the name
711
                for ($j = $i; $j < $count; $j++) {
712
                    if ($tokens[$j][0] === T_STRING) {
713
                        // collect all strings and connect them
714
                        $namespace .= '\\' . $tokens[$j][1];
715
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
716
                    } elseif ($tokens[$j] === '{' || $tokens[$j] === ';') {
717
                        // break if we clearly reached the end of the namespace declaration
718
                        break;
719
                    }
720
                }
721
                continue;
722
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
723
            } elseif ($i > 1
724
                && $tokens[$i - 2][0] === T_CLASS
725
                && $tokens[$i - 1][0] === T_WHITESPACE
726
                && $tokens[$i][0] === T_STRING
727
            ) {
728
                if ($this->findAnnotations($file, array(Aspect::ANNOTATION))) {
729
                    $type = 'aspect';
730
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
731
                } else {
732
                    $type = 'class';
733
                }
734
735
                $stuctureName = $tokens[$i][1];
736
                break;
737
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
738
            } elseif ($i > 1
739
                && $tokens[$i - 2][0] === T_TRAIT
740
                && $tokens[$i - 1][0] === T_WHITESPACE
741
                && $tokens[$i][0] === T_STRING
742
            ) {
743
                $type = 'trait';
744
                $stuctureName = $tokens[$i][1];
745
                break;
746
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
747
            } elseif ($i > 1
748
                && $tokens[$i - 2][0] === T_INTERFACE
749
                && $tokens[$i - 1][0] === T_WHITESPACE
750
                && $tokens[$i][0] === T_STRING
751
            ) {
752
                $type = 'interface';
753
                $stuctureName = $tokens[$i][1];
754
                break;
755
            }
756
        }
757
758
        // Check what we got and return it accordingly. We will return an ugly array of the sort
759
        // array(<STRUCTURE_TYPE>, <STRUCTURE_NAME>).
760
        if (empty($stuctureName)) {
761
            return false;
762
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
763
        } elseif (empty($namespace)) {
764
            // We got no namespace, so just use the structure name
765
            return array($type, $stuctureName);
766
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
767
        } else {
768
            // We got both, so combine it.
769
            return array($type, ltrim($namespace . '\\' . $stuctureName, '\\'));
770
        }
771
    }
772
773
    /**
774
     * Will load a serialized map from the storage file, if it exists
775
     *
776
     * @return bool
777
     */
778
    protected function load()
779
    {
780
        // Can we read the intended path?
781
        if (is_readable($this->mapPath)) {
782
            // Get the map
783
            $this->map = unserialize(file_get_contents($this->mapPath));
784
785
            // Get the version and remove it from the map
786
            $this->version = $this->map['version'];
787
            unset($this->map['version']);
788
789
            return true;
790
        }
791
792
        return false;
793
    }
794
795
    /**
796
     * Will save this map into the storage file.
797
     *
798
     * @return bool
799
     */
800
    protected function save()
801
    {
802
        // Add the version to the map
803
        $this->map['version'] = $this->version;
804
805
        // try to serialize into the known path
806
        if (file_put_contents($this->mapPath, serialize((array) $this->map)) >= 0) {
807
            // Remove the version entry and return the result
808
            unset($this->map['version']);
809
810
            return true;
811
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
812
        } else {
813
            // Remove the version entry and return the result
814
            unset($this->map['version']);
815
816
            return false;
817
        }
818
    }
819
}
820