Skip to content

Commit 18ce7f9

Browse files
committed
Improve build step error reporting for ServiceLocator.
1 parent ff68f63 commit 18ce7f9

4 files changed

Lines changed: 84 additions & 1 deletion

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace phpweb\Framework\Services\Dummy;
4+
5+
use phpweb\Framework\Services\Service;
6+
7+
#[Service(factory: [DummyServiceFactory::class, 'build'], public: true)]
8+
class DummyService
9+
{
10+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace phpweb\Framework\Services\Dummy;
4+
5+
use phpweb\Framework\Services\Service;
6+
7+
#[Service]
8+
class DummyServiceFactory
9+
{
10+
public function build(): DummyService
11+
{
12+
return new DummyService();
13+
}
14+
}

src/Framework/Services/ServiceLocator.php

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66
use Error;
77
use phpweb\Framework\Build\BuildContext;
88
use phpweb\Framework\Build\BuildStep;
9+
use phpweb\Framework\Build\BuildStepFailureException;
910
use phpweb\Framework\Build\BuildTools;
1011
use phpweb\Framework\Build\ReflectionHelpers;
1112
use phpweb\Framework\IO\FS;
1213
use phpweb\Framework\Routing\Route;
1314
use phpweb\ProjectGlobals;
1415
use Psr\Container\ContainerInterface;
1516
use ReflectionClass;
17+
use ReflectionException;
1618
use Symfony\Component\DependencyInjection\Container;
1719
use Symfony\Component\DependencyInjection\ContainerBuilder;
1820
use Symfony\Component\DependencyInjection\Definition;
@@ -49,13 +51,51 @@ public static function build(BuildContext $context): void
4951
if ($attr && $attr->factory) {
5052
[$factoryClassId, $factoryMethod] = $attr->factory;
5153

52-
if (!new ReflectionClass($factoryClassId)->getMethod($factoryMethod)->isStatic()) {
54+
try {
55+
$factoryClassReflection = new ReflectionClass($factoryClassId);
56+
}
57+
/** @phpstan-ignore-next-line - It doesn't know ReflectionClass constructor can throw */
58+
catch (ReflectionException) {
59+
throw new BuildStepFailureException(
60+
message: "#[Service] specifies factory class '$factoryClassId' that does not exist",
61+
target: $class,
62+
);
63+
}
64+
65+
try {
66+
$factoryMethodReflection = $factoryClassReflection->getMethod($factoryMethod);
67+
} catch (ReflectionException) {
68+
throw new BuildStepFailureException(
69+
message: "#[Service] specifies factory class method {$factoryClassId}::{$factoryMethod} that does not exist'",
70+
target: $class,
71+
);
72+
}
73+
74+
if (!$factoryMethodReflection->isPublic()) {
75+
throw new BuildStepFailureException(
76+
message: "#[Service] specifies factory class method {$factoryClassId}::{$factoryMethod} that is not public'",
77+
target: $class,
78+
);
79+
}
80+
81+
/* non-static methods must be on a class that is by itself a service */
82+
if (!$factoryMethodReflection->isStatic()) {
83+
$targetFactoryClass = new ReflectionClass($factoryClassId);
84+
if (!ReflectionHelpers::getAttribute($targetFactoryClass, Service::class)) {
85+
throw new BuildStepFailureException(
86+
message: "#[Service] specifies factory class '$factoryClassId' that is not registered a service",
87+
target: $class,
88+
);
89+
}
90+
91+
/* by using a reference we call upon the service */
5392
$factoryClassId = new Reference($factoryClassId);
5493
}
5594

5695
$definition->setFactory([$factoryClassId, $factoryMethod]);
5796
}
5897

98+
$context->logger->debug("Registered {$class->getName()} as a service");
5999
$builder->setDefinition($classId, $definition);
60100
}
61101

tests/Unit/ServicesTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpweb\Test\Unit;
6+
7+
use PHPUnit\Framework;
8+
use phpweb\Framework\Services\Dummy\DummyService;
9+
use phpweb\Framework\Services\ServiceLocator;
10+
11+
final class ServicesTest extends Framework\TestCase
12+
{
13+
public function testBasicService(): void
14+
{
15+
$instance = ServiceLocator::make()->get(DummyService::class);
16+
17+
self::assertInstanceOf(DummyService::class, $instance);
18+
}
19+
}

0 commit comments

Comments
 (0)