Issues (245)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/StructureMap.php (15 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
 * \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
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
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
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
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
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
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
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
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
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
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
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
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
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
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