diff --git a/src/Controllers/Controller.php b/src/Controllers/Controller.php index 880c8a9..a0e9b1d 100644 --- a/src/Controllers/Controller.php +++ b/src/Controllers/Controller.php @@ -7,7 +7,7 @@ namespace Siteworxpro\App\Controllers; use League\Route\Http\Exception\NotFoundException; use Nyholm\Psr7\ServerRequest; use Psr\Http\Message\ResponseInterface; -use Siteworxpro\App\Facades\Logger; +use Siteworxpro\App\Services\Facades\Logger; use Siteworxpro\App\Traefik\ProtocolEnum; abstract class Controller implements ControllerInterface diff --git a/src/Controllers/MiddlewaresController.php b/src/Controllers/MiddlewaresController.php index eeff4bd..6e95290 100644 --- a/src/Controllers/MiddlewaresController.php +++ b/src/Controllers/MiddlewaresController.php @@ -7,8 +7,8 @@ namespace Siteworxpro\App\Controllers; use Nyholm\Psr7\ServerRequest; use Psr\Http\Message\ResponseInterface; use Siteworxpro\App\Http\JsonResponseFactory; +use Siteworxpro\App\Services\Facades\RedisClient; use Siteworxpro\App\Traefik\EntityEnum; -use Siteworxpro\App\Facades\RedisClient; class MiddlewaresController extends Controller { diff --git a/src/Controllers/RoutesController.php b/src/Controllers/RoutesController.php index 1785719..a1db33e 100644 --- a/src/Controllers/RoutesController.php +++ b/src/Controllers/RoutesController.php @@ -7,8 +7,8 @@ namespace Siteworxpro\App\Controllers; use Nyholm\Psr7\ServerRequest; use Psr\Http\Message\ResponseInterface; use Siteworxpro\App\Http\JsonResponseFactory; +use Siteworxpro\App\Services\Facades\RedisClient; use Siteworxpro\App\Traefik\EntityEnum; -use Siteworxpro\App\Facades\RedisClient; class RoutesController extends Controller { diff --git a/src/Controllers/ServicesController.php b/src/Controllers/ServicesController.php index a50c040..705f8f8 100644 --- a/src/Controllers/ServicesController.php +++ b/src/Controllers/ServicesController.php @@ -7,8 +7,8 @@ namespace Siteworxpro\App\Controllers; use Nyholm\Psr7\ServerRequest; use Psr\Http\Message\ResponseInterface; use Siteworxpro\App\Http\JsonResponseFactory; +use Siteworxpro\App\Services\Facades\RedisClient; use Siteworxpro\App\Traefik\EntityEnum; -use Siteworxpro\App\Facades\RedisClient; class ServicesController extends Controller { diff --git a/src/Http/Middleware/CorsMiddleware.php b/src/Http/Middleware/CorsMiddleware.php index 96584f1..d8f6c23 100644 --- a/src/Http/Middleware/CorsMiddleware.php +++ b/src/Http/Middleware/CorsMiddleware.php @@ -9,7 +9,7 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; -use Siteworxpro\App\Facades\Config; +use Siteworxpro\App\Services\Facades\Config; /** * Class CorsMiddleware diff --git a/src/Server.php b/src/Server.php index b591a38..e652570 100644 --- a/src/Server.php +++ b/src/Server.php @@ -6,7 +6,6 @@ namespace Siteworxpro\App; use Illuminate\Container\Container; use Illuminate\Database\Capsule\Manager; -use Illuminate\Support\Facades\Facade; use League\Route\Http\Exception\MethodNotAllowedException; use League\Route\Http\Exception\NotFoundException; use League\Route\RouteGroup; @@ -16,10 +15,11 @@ use Nyholm\Psr7\Factory\Psr17Factory; use Siteworxpro\App\Controllers\MiddlewaresController; use Siteworxpro\App\Controllers\RoutesController; use Siteworxpro\App\Controllers\ServicesController; -use Siteworxpro\App\Facades\Config; -use Siteworxpro\App\Facades\Logger; use Siteworxpro\App\Http\JsonResponseFactory; use Siteworxpro\App\Http\Middleware\CorsMiddleware; +use Siteworxpro\App\Services\Facade; +use Siteworxpro\App\Services\Facades\Config; +use Siteworxpro\App\Services\Facades\Logger; use Spiral\RoadRunner\Http\PSR7Worker; use Spiral\RoadRunner\Worker; @@ -67,7 +67,7 @@ class Server private function boot(): void { $container = new Container(); - Facade::setFacadeApplication($container); + Facade::setFacadeContainer($container); $this->worker = new PSR7Worker( Worker::create(), diff --git a/src/Services/Facade.php b/src/Services/Facade.php new file mode 100644 index 0000000..355259a --- /dev/null +++ b/src/Services/Facade.php @@ -0,0 +1,308 @@ +resolved($accessor) === true) { + $callback(static::getFacadeRoot(), static::$container); + } + + static::$container->afterResolving($accessor, function ($service, $app) use ($callback) { + $callback($service, $app); + }); + } + + /** + * Convert the facade into a Mockery spy. + * + * @return MockInterface + */ + public static function spy(): MockInterface + { + if (! static::isMock()) { + $class = static::getMockableClass(); + + return tap($class ? Mockery::spy($class) : Mockery::spy(), function ($spy) { + static::swap($spy); + }); + } + + throw new \RuntimeException('Cannot spy on an existing mock instance.'); + } + + /** + * Initiate a partial mock on the facade. + * + * @return MockInterface + */ + public static function partialMock(): MockInterface + { + $name = static::getFacadeAccessor(); + + $mock = static::isMock() + ? static::$resolvedInstance[$name] + : static::createFreshMockInstance(); + + return $mock->makePartial(); + } + + /** + * Initiate a mock expectation on the facade. + * + * @return Expectation|ExpectationInterface + */ + public static function shouldReceive(): Mockery\Expectation | Mockery\ExpectationInterface + { + $name = static::getFacadeAccessor(); + + $mock = static::isMock() + ? static::$resolvedInstance[$name] + : static::createFreshMockInstance(); + + return $mock->shouldReceive(...func_get_args()); + } + + /** + * Initiate a mock expectation on the facade. + * + * @return Expectation|ExpectationInterface + */ + public static function expects(): Mockery\Expectation | Mockery\ExpectationInterface + { + $name = static::getFacadeAccessor(); + + $mock = static::isMock() + ? static::$resolvedInstance[$name] + : static::createFreshMockInstance(); + + return $mock->expects(...func_get_args()); + } + + /** + * Create a fresh mock instance for the given class. + * + * @return MockInterface|LegacyMockInterface + */ + protected static function createFreshMockInstance(): MockInterface | LegacyMockInterface + { + return tap(static::createMock(), function ($mock) { + static::swap($mock); + + $mock->shouldAllowMockingProtectedMethods(); + }); + } + + /** + * Create a fresh mock instance for the given class. + * + * @return MockInterface + */ + protected static function createMock(): MockInterface + { + $class = static::getMockableClass(); + + return $class ? Mockery::mock($class) : Mockery::mock(); + } + + /** + * Determines whether a mock is set as the instance of the facade. + * + * @return bool + */ + protected static function isMock(): bool + { + $name = static::getFacadeAccessor(); + + return isset(static::$resolvedInstance[$name]) && + static::$resolvedInstance[$name] instanceof LegacyMockInterface; + } + + /** + * Get the mockable class for the bound instance. + * + * @return string|null + */ + protected static function getMockableClass(): ?string + { + if ($root = static::getFacadeRoot()) { + return get_class($root); + } + + return null; + } + + /** + * Hotswap the underlying instance behind the facade. + * + * @param mixed $instance + * @return void + */ + public static function swap(mixed $instance): void + { + static::$resolvedInstance[static::getFacadeAccessor()] = $instance; + + if (isset(static::$container)) { + static::$container->instance(static::getFacadeAccessor(), $instance); + } + } + + /** + * Determines whether a "fake" has been set as the facade instance. + * + * @return bool + */ + public static function isFake(): bool + { + $name = static::getFacadeAccessor(); + + return isset(static::$resolvedInstance[$name]) && + static::$resolvedInstance[$name] instanceof Fake; + } + + /** + * Get the root object behind the facade. + * + * @return mixed + */ + public static function getFacadeRoot(): mixed + { + return static::resolveFacadeInstance(static::getFacadeAccessor()); + } + + /** + * Get the registered name of the component. + * + * @return string + * + * @throws \RuntimeException + */ + protected static function getFacadeAccessor(): string + { + throw new \RuntimeException('Facade does not implement getFacadeAccessor method.'); + } + + /** + * Resolve the facade root instance from the container. + * + * @param string $name + * @return mixed + */ + protected static function resolveFacadeInstance(string $name): mixed + { + if (isset(static::$resolvedInstance[$name])) { + return static::$resolvedInstance[$name]; + } + + if (static::$container) { + if (static::$cached) { + return static::$resolvedInstance[$name] = static::$container[$name]; + } + + return static::$container[$name]; + } + + return null; + } + + /** + * Clear a resolved facade instance. + * + * @param string $name + * @return void + */ + public static function clearResolvedInstance(string $name): void + { + unset(static::$resolvedInstance[$name]); + } + + /** + * Clear all of the resolved instances. + * + * @return void + */ + public static function clearResolvedInstances(): void + { + static::$resolvedInstance = []; + } + + /** + * Get the application instance behind the facade. + */ + public static function getFacadeContainer(): ?Container + { + return static::$container; + } + + /** + * Set the application instance. + * + * @param Container $container + * @return void + */ + public static function setFacadeContainer(Container $container): void + { + static::$container = $container; + } + + /** + * Handle dynamic, static calls to the object. + * + * @param string $method + * @param array $args + * @return mixed + * + * @throws \RuntimeException + */ + public static function __callStatic(string $method, array $args) + { + $instance = static::getFacadeRoot(); + + if (! $instance) { + throw new \RuntimeException('A facade root has not been set.'); + } + + return $instance->$method(...$args); + } +} diff --git a/src/Facades/Config.php b/src/Services/Facades/Config.php similarity index 97% rename from src/Facades/Config.php rename to src/Services/Facades/Config.php index 339bdd6..eedf505 100644 --- a/src/Facades/Config.php +++ b/src/Services/Facades/Config.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Siteworxpro\App\Facades; +namespace Siteworxpro\App\Services\Facades; use Illuminate\Contracts\Container\BindingResolutionException; use Illuminate\Support\Facades\Facade; diff --git a/src/Facades/Logger.php b/src/Services/Facades/Logger.php similarity index 96% rename from src/Facades/Logger.php rename to src/Services/Facades/Logger.php index 0a5fb33..efa4ddf 100644 --- a/src/Facades/Logger.php +++ b/src/Services/Facades/Logger.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Siteworxpro\App\Facades; +namespace Siteworxpro\App\Services\Facades; use Illuminate\Support\Facades\Facade; use RoadRunner\Logger\Logger as RRLogger; diff --git a/src/Facades/Redis.php b/src/Services/Facades/Redis.php similarity index 96% rename from src/Facades/Redis.php rename to src/Services/Facades/Redis.php index 04ab0d1..3057d96 100644 --- a/src/Facades/Redis.php +++ b/src/Services/Facades/Redis.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Siteworxpro\App\Facades; +namespace Siteworxpro\App\Services\Facades; use Illuminate\Support\Facades\Facade; use Predis\Client; diff --git a/src/Facades/RedisClient.php b/src/Services/Facades/RedisClient.php similarity index 97% rename from src/Facades/RedisClient.php rename to src/Services/Facades/RedisClient.php index 7847104..aac0540 100644 --- a/src/Facades/RedisClient.php +++ b/src/Services/Facades/RedisClient.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Siteworxpro\App\Facades; +namespace Siteworxpro\App\Services\Facades; use Illuminate\Support\Facades\Facade; use Siteworxpro\App\Traefik\EntityEnum; diff --git a/src/Traefik/RedisClient.php b/src/Traefik/RedisClient.php index 3cb67e3..0282118 100644 --- a/src/Traefik/RedisClient.php +++ b/src/Traefik/RedisClient.php @@ -6,7 +6,7 @@ namespace Siteworxpro\App\Traefik; use Illuminate\Support\Collection; use Predis\Collection\Iterator\Keyspace; -use Siteworxpro\App\Facades\Redis; +use Siteworxpro\App\Services\Facades\Redis; /** * Class RedisClient diff --git a/tests/Controllers/ControllerTest.php b/tests/Controllers/ControllerTest.php index c98db77..5ecbcb5 100644 --- a/tests/Controllers/ControllerTest.php +++ b/tests/Controllers/ControllerTest.php @@ -6,11 +6,7 @@ namespace Siteworxpro\Tests\Controllers; use League\Route\Http\Exception\NotFoundException; use Nyholm\Psr7\ServerRequest; -use PHPUnit\Framework\MockObject\Exception; -use Psr\Log\LoggerInterface; use Siteworxpro\App\Controllers\Controller; -use Siteworxpro\App\Facades\Logger; -use Siteworxpro\App\Traefik\ProtocolEnum; use Siteworxpro\Tests\Unit; class ControllerTest extends Unit diff --git a/tests/Controllers/MiddlewareControllerTest.php b/tests/Controllers/MiddlewareControllerTest.php index e929ba6..8954a13 100644 --- a/tests/Controllers/MiddlewareControllerTest.php +++ b/tests/Controllers/MiddlewareControllerTest.php @@ -6,7 +6,7 @@ namespace Siteworxpro\Tests\Controllers; use Nyholm\Psr7\ServerRequest; use Siteworxpro\App\Controllers\MiddlewaresController; -use Siteworxpro\App\Facades\RedisClient; +use Siteworxpro\App\Services\Facades\RedisClient; use Siteworxpro\App\Traefik\EntityEnum; use Siteworxpro\App\Traefik\ProtocolEnum; use Siteworxpro\Tests\Unit; diff --git a/tests/Controllers/RoutesControllerTest.php b/tests/Controllers/RoutesControllerTest.php index 2336660..52681fa 100644 --- a/tests/Controllers/RoutesControllerTest.php +++ b/tests/Controllers/RoutesControllerTest.php @@ -6,7 +6,7 @@ namespace Siteworxpro\Tests\Controllers; use Nyholm\Psr7\ServerRequest; use Siteworxpro\App\Controllers\RoutesController; -use Siteworxpro\App\Facades\RedisClient; +use Siteworxpro\App\Services\Facades\RedisClient; use Siteworxpro\App\Traefik\EntityEnum; use Siteworxpro\App\Traefik\ProtocolEnum; use Siteworxpro\Tests\Unit; diff --git a/tests/Controllers/ServicesControllerTest.php b/tests/Controllers/ServicesControllerTest.php index a962684..8be74bd 100644 --- a/tests/Controllers/ServicesControllerTest.php +++ b/tests/Controllers/ServicesControllerTest.php @@ -6,7 +6,7 @@ namespace Siteworxpro\Tests\Controllers; use Nyholm\Psr7\ServerRequest; use Siteworxpro\App\Controllers\ServicesController; -use Siteworxpro\App\Facades\RedisClient; +use Siteworxpro\App\Services\Facades\RedisClient; use Siteworxpro\App\Traefik\EntityEnum; use Siteworxpro\App\Traefik\ProtocolEnum; use Siteworxpro\Tests\Unit; diff --git a/tests/Http/Middleware/CorsMiddlewareTest.php b/tests/Http/Middleware/CorsMiddlewareTest.php index 0342b2e..3163ec4 100644 --- a/tests/Http/Middleware/CorsMiddlewareTest.php +++ b/tests/Http/Middleware/CorsMiddlewareTest.php @@ -7,8 +7,8 @@ namespace Siteworxpro\Tests\Http\Middleware; use Nyholm\Psr7\Response; use Nyholm\Psr7\ServerRequest; use Psr\Http\Server\RequestHandlerInterface; -use Siteworxpro\App\Facades\Config; use Siteworxpro\App\Http\Middleware\CorsMiddleware; +use Siteworxpro\App\Services\Facades\Config; use Siteworxpro\Tests\Unit; class CorsMiddlewareTest extends Unit diff --git a/tests/Traefik/MiddlewareServiceTest.php b/tests/Traefik/MiddlewareServiceTest.php index dfa8cac..a972f8f 100644 --- a/tests/Traefik/MiddlewareServiceTest.php +++ b/tests/Traefik/MiddlewareServiceTest.php @@ -6,7 +6,7 @@ namespace Siteworxpro\Tests\Traefik; use Mockery; use Predis\Command\FactoryInterface; -use Siteworxpro\App\Facades\Redis; +use Siteworxpro\App\Services\Facades\Redis; use Siteworxpro\App\Traefik\ProtocolEnum; use Siteworxpro\App\Traefik\RedisClient; use Siteworxpro\Tests\Unit; diff --git a/tests/Traefik/RedisClientRoutersTest.php b/tests/Traefik/RedisClientRoutersTest.php index 4404f02..609ee37 100644 --- a/tests/Traefik/RedisClientRoutersTest.php +++ b/tests/Traefik/RedisClientRoutersTest.php @@ -6,8 +6,8 @@ namespace Siteworxpro\Tests\Traefik; use Mockery; use Predis\Command\FactoryInterface; -use Siteworxpro\App\Facades\Redis; -use Siteworxpro\App\Facades\RedisClient; +use Siteworxpro\App\Services\Facades\Redis; +use Siteworxpro\App\Services\Facades\RedisClient; use Siteworxpro\App\Traefik\EntityEnum; use Siteworxpro\App\Traefik\ProtocolEnum; use Siteworxpro\Tests\Unit; diff --git a/tests/Traefik/RedisClientServiceTest.php b/tests/Traefik/RedisClientServiceTest.php index b332edc..bf72319 100644 --- a/tests/Traefik/RedisClientServiceTest.php +++ b/tests/Traefik/RedisClientServiceTest.php @@ -6,7 +6,7 @@ namespace Siteworxpro\Tests\Traefik; use Mockery; use Predis\Command\FactoryInterface; -use Siteworxpro\App\Facades\Redis; +use Siteworxpro\App\Services\Facades\Redis; use Siteworxpro\App\Traefik\EntityEnum; use Siteworxpro\App\Traefik\ProtocolEnum; use Siteworxpro\App\Traefik\RedisClient; diff --git a/tests/Unit.php b/tests/Unit.php index 9a32b17..7bf9c56 100644 --- a/tests/Unit.php +++ b/tests/Unit.php @@ -8,8 +8,8 @@ use Illuminate\Container\Container; use Illuminate\Support\Facades\Facade; use Illuminate\Support\Facades\Redis; use PHPUnit\Framework\TestCase; -use Siteworxpro\App\Facades\Config; -use Siteworxpro\App\Facades\RedisClient; +use Siteworxpro\App\Services\Facades\Config; +use Siteworxpro\App\Services\Facades\RedisClient; abstract class Unit extends TestCase {