Skip to content

Commit b838279

Browse files
Added php files via file upload
1 parent c03107c commit b838279

15 files changed

+924
-4
lines changed

src/.gitignore

Lines changed: 0 additions & 2 deletions
This file was deleted.

src/AnnotationLoader.php

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of Biurad opensource projects.
7+
*
8+
* PHP version 7.2 and above required
9+
*
10+
* @author Divine Niiquaye Ibok <divineibok@gmail.com>
11+
* @copyright 2019 Biurad Group (https://biurad.com/)
12+
* @license https://opensource.org/licenses/BSD-3-Clause License
13+
*
14+
* For the full copyright and license information, please view the LICENSE
15+
* file that was distributed with this source code.
16+
*/
17+
18+
namespace Biurad\Annotations;
19+
20+
use Doctrine\Common\Annotations\Reader as AnnotationReader;
21+
use Doctrine\Common\Annotations\SimpleAnnotationReader;
22+
use FilesystemIterator;
23+
use RecursiveDirectoryIterator;
24+
use RecursiveIteratorIterator;
25+
use ReflectionClass;
26+
use ReflectionMethod;
27+
use ReflectionProperty;
28+
use Reflector;
29+
use RegexIterator;
30+
31+
class AnnotationLoader implements LoaderInterface
32+
{
33+
/** @var null|AnnotationReader */
34+
private $annotation;
35+
36+
/** @var ListenerInterface[] */
37+
private $listeners;
38+
39+
/** @var string[] */
40+
private $resources = [];
41+
42+
/**
43+
* @param null|AnnotationReader $reader
44+
*/
45+
public function __construct(?AnnotationReader $reader = null)
46+
{
47+
$this->annotation = $reader;
48+
49+
if (null === $reader && \interface_exists(AnnotationReader::class)) {
50+
$this->annotation = new SimpleAnnotationReader();
51+
}
52+
}
53+
54+
/**
55+
* {@inheritdoc}
56+
*/
57+
public function attachListener(ListenerInterface ...$listeners): void
58+
{
59+
foreach ($listeners as $listener) {
60+
if ($this->annotation instanceof SimpleAnnotationReader) {
61+
$this->annotation->addNamespace($listener->getAnnotation());
62+
}
63+
64+
$this->listeners[] = $listener;
65+
}
66+
}
67+
68+
/**
69+
* {@inheritdoc}
70+
*/
71+
public function attach(string ...$resources): void
72+
{
73+
foreach ($resources as $resource) {
74+
$this->resources[] = $resource;
75+
}
76+
}
77+
78+
/**
79+
* {@inheritdoc}
80+
*/
81+
public function load(): iterable
82+
{
83+
$annotations = [];
84+
85+
foreach ($this->resources as $resource) {
86+
foreach ($this->listeners as $listener) {
87+
if (\class_exists($resource) || \is_dir($resource)) {
88+
$annotations += $this->findAnnotations($resource, $listener);
89+
90+
continue;
91+
}
92+
93+
if (!\file_exists($resource) || \is_dir($resource)) {
94+
continue;
95+
}
96+
97+
(function () use ($resource): void {
98+
require $resource;
99+
})->call($listener->getBinding());
100+
}
101+
}
102+
103+
foreach ($this->listeners as $listener) {
104+
if (null !== $found = $listener->onAnnotation($annotations)) {
105+
yield $found;
106+
}
107+
}
108+
}
109+
110+
/**
111+
* Finds annotations in the given resource
112+
*
113+
* @param string $resource
114+
*
115+
* @return array<string,array<string,mixed>>
116+
*/
117+
private function findAnnotations(string $resource, ListenerInterface $listener): array
118+
{
119+
$classes = $annotations = [];
120+
121+
if (\is_dir($resource)) {
122+
$classes = \array_merge($this->findClasses($resource), $classes);
123+
} elseif (\class_exists($resource)) {
124+
$classes[] = $resource;
125+
}
126+
127+
foreach ($classes as $class) {
128+
$classReflection = new ReflectionClass($class);
129+
$className = $classReflection->getName();
130+
131+
if ($classReflection->isAbstract()) {
132+
throw new InvalidAnnotationException(\sprintf(
133+
'Annotations from class "%s" cannot be read as it is abstract.',
134+
$classReflection->getName()
135+
));
136+
}
137+
138+
foreach ($this->getAnnotations($classReflection, $listener) as $annotation) {
139+
$annotations[$className]['class'] = $annotation;
140+
}
141+
142+
$reflections = \array_merge($classReflection->getMethods(), $classReflection->getProperties());
143+
144+
foreach ($reflections as $reflection) {
145+
if ($reflection instanceof ReflectionMethod && $reflection->isAbstract()) {
146+
continue;
147+
}
148+
149+
foreach ($this->getAnnotations($reflection, $listener) as $annotation) {
150+
if ($reflection instanceof ReflectionMethod) {
151+
$annotations[$className]['method'][] = [$reflection, $annotation];
152+
153+
continue;
154+
}
155+
156+
$annotations[$className]['property'][] = [$reflection, $annotation];
157+
}
158+
}
159+
}
160+
161+
\gc_mem_caches();
162+
163+
return $annotations;
164+
}
165+
166+
/**
167+
* @param ReflectionClass|ReflectionMethod|ReflectionProperty $reflection
168+
*
169+
* @return iterable<object>
170+
*/
171+
private function getAnnotations(Reflector $reflection, ListenerInterface $listener): iterable
172+
{
173+
$annotationClass = $listener->getAnnotation();
174+
$annotations = [];
175+
176+
if (\PHP_VERSION_ID >= 80000) {
177+
foreach ($reflection->getAttributes($annotationClass) as $attribute) {
178+
yield $attribute->newInstance();
179+
}
180+
}
181+
182+
if (null === $this->annotation) {
183+
return;
184+
}
185+
186+
switch (true) {
187+
case $reflection instanceof ReflectionClass:
188+
$annotations = $this->annotation->getClassAnnotations($reflection);
189+
190+
break;
191+
192+
case $reflection instanceof ReflectionMethod:
193+
$annotations = $this->annotation->getMethodAnnotations($reflection);
194+
195+
break;
196+
197+
case $reflection instanceof ReflectionProperty:
198+
$annotations = $this->annotation->getPropertyAnnotations($reflection);
199+
200+
break;
201+
}
202+
203+
foreach ($annotations as $annotation) {
204+
if ($annotation instanceof $annotationClass) {
205+
yield $annotation;
206+
}
207+
}
208+
}
209+
210+
/**
211+
* Finds classes in the given resource directory
212+
*
213+
* @param string $resource
214+
*
215+
* @return string[]
216+
*/
217+
private function findClasses(string $resource): array
218+
{
219+
$files = $this->findFiles($resource);
220+
$declared = \get_declared_classes();
221+
222+
foreach ($files as $file) {
223+
require_once $file;
224+
}
225+
226+
return \array_diff(\get_declared_classes(), $declared);
227+
}
228+
229+
/**
230+
* Finds files in the given resource
231+
*
232+
* @param string $resource
233+
*
234+
* @return string[]
235+
*/
236+
private function findFiles(string $resource): array
237+
{
238+
$flags = FilesystemIterator::CURRENT_AS_PATHNAME;
239+
240+
$directory = new RecursiveDirectoryIterator($resource, $flags);
241+
$iterator = new RecursiveIteratorIterator($directory);
242+
$files = new RegexIterator($iterator, '/\.php$/');
243+
244+
return \iterator_to_array($files);
245+
}
246+
}

src/InvalidAnnotationException.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of Biurad opensource projects.
7+
*
8+
* PHP version 7.2 and above required
9+
*
10+
* @author Divine Niiquaye Ibok <divineibok@gmail.com>
11+
* @copyright 2019 Biurad Group (https://biurad.com/)
12+
* @license https://opensource.org/licenses/BSD-3-Clause License
13+
*
14+
* For the full copyright and license information, please view the LICENSE
15+
* file that was distributed with this source code.
16+
*/
17+
18+
namespace Biurad\Annotations;
19+
20+
use InvalidArgumentException;
21+
22+
class InvalidAnnotationException extends InvalidArgumentException
23+
{
24+
}

src/ListenerInterface.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of Biurad opensource projects.
7+
*
8+
* PHP version 7.2 and above required
9+
*
10+
* @author Divine Niiquaye Ibok <divineibok@gmail.com>
11+
* @copyright 2019 Biurad Group (https://biurad.com/)
12+
* @license https://opensource.org/licenses/BSD-3-Clause License
13+
*
14+
* For the full copyright and license information, please view the LICENSE
15+
* file that was distributed with this source code.
16+
*/
17+
18+
namespace Biurad\Annotations;
19+
20+
interface ListenerInterface
21+
{
22+
/**
23+
* This method utilises found annotations and return collector.
24+
*
25+
* @param array<string,array<string,mixed>> $annotations
26+
*
27+
* @return mixed
28+
*/
29+
public function onAnnotation(array $annotations);
30+
31+
/**
32+
* The annotation class to find
33+
*
34+
* @return string
35+
*/
36+
public function getAnnotation(): string;
37+
38+
/**
39+
* Add a binding, so we can load from php files
40+
* which are not class type.
41+
*
42+
* @return object
43+
*/
44+
public function getBinding(): object;
45+
}

src/LoaderInterface.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of Biurad opensource projects.
7+
*
8+
* PHP version 7.2 and above required
9+
*
10+
* @author Divine Niiquaye Ibok <divineibok@gmail.com>
11+
* @copyright 2019 Biurad Group (https://biurad.com/)
12+
* @license https://opensource.org/licenses/BSD-3-Clause License
13+
*
14+
* For the full copyright and license information, please view the LICENSE
15+
* file that was distributed with this source code.
16+
*/
17+
18+
namespace Biurad\Annotations;
19+
20+
interface LoaderInterface
21+
{
22+
/**
23+
* Attache(s) the given resource(s) to the loader
24+
*
25+
* @param string ...$resources type of class string, file, or directory
26+
*/
27+
public function attach(string ...$resources): void;
28+
29+
/**
30+
* Attache(s) the given listener(s) to the loader
31+
*
32+
* @param ListenerInterface ...$listeners
33+
*/
34+
public function attachListener(ListenerInterface ...$listeners): void;
35+
36+
/**
37+
* Loads routes from attached resources
38+
*
39+
* @return iterable|mixed[]
40+
*/
41+
public function load(): iterable;
42+
}

tests/.gitignore

Lines changed: 0 additions & 2 deletions
This file was deleted.

0 commit comments

Comments
 (0)