Completed
Push — master ( 5588f7...92204b )
by Vitaly
03:02
created

MetadataBuilder   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 153
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 19
lcom 1
cbo 5
dl 0
loc 153
ccs 39
cts 39
cp 1
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
A loadFromPaths() 0 10 2
A loadFromClassNames() 0 14 3
B getDefinedClasses() 0 20 7
A loadFromCode() 0 10 2
A build() 0 17 4
1
<?php declare(strict_types = 1);
2
/**
3
 * Created by PhpStorm.
4
 * User: root
5
 * Date: 02.08.16
6
 * Time: 0:46.
7
 */
8
namespace samsonframework\container;
9
10
use samsonframework\container\metadata\ClassMetadata;
11
use samsonframework\container\resolver\ResolverInterface;
12
use samsonframework\di\Container;
13
use samsonframework\filemanager\FileManagerInterface;
14
15
/**
16
 * Class Container.
17
 */
18
class MetadataBuilder
19
{
20
    /** Controller classes scope name */
21
    const SCOPE_CONTROLLER = 'controllers';
22
23
    /** Service classes scope name */
24
    const SCOPE_SERVICES = 'services';
25
26
    /** @var string[] Collection of available container scopes */
27
    protected $scopes = [
28
        self::SCOPE_CONTROLLER => [],
29
        self::SCOPE_SERVICES => []
30
    ];
31
32
    /** @var ClassMetadata[] Collection of classes metadata */
33
    protected $classMetadata = [];
34
35
    /** @var FileManagerInterface */
36
    protected $fileManger;
37
38
    /** @var ResolverInterface */
39
    protected $classResolver;
40
41
    /** @var Container */
42
    protected $diContainer;
43
44
    /**
45
     * Container constructor.
46
     *
47
     * @param FileManagerInterface $fileManger
48
     * @param ResolverInterface    $classResolver
49
     * @param Container            $diContainer
50
     */
51 5
    public function __construct(
52
        FileManagerInterface $fileManger,
53
        ResolverInterface $classResolver,
54
        Container $diContainer
55
    )
56
    {
0 ignored issues
show
Coding Style introduced by
The closing parenthesis and the opening brace of a multi-line function declaration must be on the same line
Loading history...
57 5
        $this->diContainer = $diContainer;
58 5
        $this->fileManger = $fileManger;
59 5
        $this->classResolver = $classResolver;
60 5
    }
61
62
    /**
63
     * Load classes from paths.
64
     *
65
     * @param array $paths Paths for importing
66
     *
67
     * @return $this
68
     */
69 1
    public function loadFromPaths(array $paths)
70
    {
71
        // Iterate all paths and get files
72 1
        foreach ($this->fileManger->scan($paths, ['php']) as $phpFile) {
73
            // Read all classes in given file
74 1
            $this->loadFromClassNames($this->getDefinedClasses(require_once($phpFile)));
75
        }
76
77 1
        return $this;
78
    }
79
80
    /**
81
     * Load classes from class names collection.
82
     *
83
     * @param string[] $classes Collection of class names for resolving
84
     *
85
     * @return $this
86
     */
87 5
    public function loadFromClassNames(array $classes)
88
    {
89
        // Read all classes in given file
90 5
        foreach ($classes as $className) {
91
            // Resolve class metadata
92 4
            $this->classMetadata[$className] = $this->classResolver->resolve(new \ReflectionClass($className));
93
            // Store class in defined scopes
94 4
            foreach ($this->classMetadata[$className]->scopes as $scope) {
95 4
                $this->scopes[$scope][] = $className;
96
            }
97
        }
98
99 5
        return $this;
100
    }
101
102
    /**
103
     * Find class names defined in PHP code.
104
     *
105
     * @param string $php PHP code for scanning
106
     *
107
     * @return string[] Collection of found class names in php code
108
     */
109 2
    protected function getDefinedClasses($php) : array
110
    {
111 2
        $classes = array();
112
113
        // Append php marker for parsing file
114 2
        $php = strpos(is_string($php) ? $php : '', '<?php') !== 0 ? '<?php ' . $php : $php;
115
116 2
        $tokens = token_get_all($php);
117
118 2
        for ($i = 2, $count = count($tokens); $i < $count; $i++) {
119 1
            if ($tokens[$i - 2][0] === T_CLASS
120 1
                && $tokens[$i - 1][0] === T_WHITESPACE
121 1
                && $tokens[$i][0] === T_STRING
122
            ) {
123 1
                $classes[] = $tokens[$i][1];
124
            }
125
        }
126
127 2
        return $classes;
128
    }
129
130
    /**
131
     * Load classes from PHP code.
132
     *
133
     * @param string $php PHP code
134
     *
135
     * @return $this
136
     */
137 1
    public function loadFromCode($php)
138
    {
139 1
        if (count($classes = $this->getDefinedClasses($php))) {
140
            // TODO: Consider writing cache file and require it
141 1
            eval($php);
142 1
            $this->loadFromClassNames($classes);
143
        }
144
145 1
        return $this;
146
    }
147
148
    /**
149
     * Build container class.
150
     *
151
     * @param string|null $containerClass
152
     */
153 2
    public function build($containerClass = null)
0 ignored issues
show
Unused Code introduced by
The parameter $containerClass is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
154
    {
155 2
        foreach ($this->classMetadata as $className => $classMetadata) {
156
            // Process constructor dependencies
157 2
            $constructorDeps = [];
158 2
            if (array_key_exists('__construct', $classMetadata->methodsMetadata)) {
159 2
                $constructorDeps = $classMetadata->methodsMetadata['__construct']->dependencies;
160
            }
161
162
            // If this class has services scope
163 2
            if (in_array($className, $this->scopes[self::SCOPE_SERVICES], true)) {
164 2
                $this->diContainer->service($className, $constructorDeps, $classMetadata->name);
0 ignored issues
show
Documentation introduced by
$classMetadata->name is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$constructorDeps is of type array, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
165
            } else { // Handle not service classes dependencies
166 2
                $this->diContainer->set($className, $constructorDeps, $classMetadata->name);
0 ignored issues
show
Documentation introduced by
$classMetadata->name is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$constructorDeps is of type array, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
167
            }
168
        }
169 2
    }
170
}
171