diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index a327b35..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,10 +0,0 @@ -version: 2 -updates: - - - package-ecosystem: "composer" - directory: "/app" - schedule: - interval: "daily" - ignore: - - dependency-name: "*" - update-types: ["version-update:semver-major", "version-update:semver-patch"] diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index b2e13fc..0000000 --- a/.github/stale.yml +++ /dev/null @@ -1,20 +0,0 @@ -# Configuration for probot-stale - https://github.com/probot/stale - -# Number of days of inactivity before an Issue or Pull Request becomes stale -daysUntilStale: 90 - -# Number of days of inactivity before an Issue or Pull Request with the stale label is closed. -daysUntilClose: 7 - -# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable -exemptLabels: [] - -# Set to true to ignore issues with an assignee (defaults to false) -exemptAssignees: true - -# Label to use when marking as stale -staleLabel: closed:stale - -# Comment to post when marking as stale. Set to `false` to disable -markComment: > - This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you have not received a response for our team (apologies for the delay) and this is still a blocker, please reply with additional information or just a ping. Thank you for your contribution! 🙇‍♂️ \ No newline at end of file diff --git a/.gitignore b/.gitignore index 11d0ed3..7315fab 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ vendor .env composer.lock .DS_Store +app/.php-cs-fixer.cache diff --git a/app/phpunit.xml.dist b/app/phpunit.xml.dist deleted file mode 100644 index a52f9a8..0000000 --- a/app/phpunit.xml.dist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - tests/Unit - - - - - - - - ./src/ - - - ./src/Events/ - ./src/Exceptions/ - - - diff --git a/app/src/Application.php b/app/src/Application.php index 5b0072c..4525424 100644 --- a/app/src/Application.php +++ b/app/src/Application.php @@ -4,7 +4,6 @@ namespace Auth0\Quickstart; -use Auth0\Quickstart\Contract\QuickstartExample; use Auth0\SDK\Auth0; use Auth0\SDK\Configuration\SdkConfiguration; use Auth0\SDK\Exception\InvalidTokenException; @@ -16,16 +15,6 @@ final class Application */ private SdkConfiguration $configuration; - /** - * An instance of the Auth0 SDK. - */ - private Auth0 $sdk; - - /** - * An instance of our application's template rendering helper class, for sending responses. - */ - private ApplicationTemplates $templates; - /** * An instance of our application's error handling class, for gracefully reporting exceptions. */ @@ -37,16 +26,14 @@ final class Application private ApplicationRouter $router; /** - * An instance of a QuickstartExample class, specified from the AUTH0_USE_EXAMPLE env. + * An instance of the Auth0 SDK. */ - private ?QuickstartExample $example = null; + private Auth0 $sdk; /** - * An array of hooks with callback functions for examples to override default behavior. - * - * @var array + * An instance of our application's template rendering helper class, for sending responses. */ - private array $exampleHooks = []; + private ApplicationTemplates $templates; /** * Setup our Quickstart application. @@ -54,7 +41,7 @@ final class Application * @param array $env Auth0 configuration imported from .env file. */ public function __construct( - array $env + array $env, ) { // Configure the SDK using our .env configuration. $this->setupAuth0($env); @@ -63,41 +50,6 @@ public function __construct( $this->templates = new ApplicationTemplates($this); $this->errorHandler = new ApplicationErrorHandler($this); $this->router = new ApplicationRouter($this); - $this->example = null; - } - - /** - * Configure the Auth0 SDK using the .env configuration. - * - * @param array $env Auth0 configuration imported from .env file. - */ - public function setupAuth0( - array $env - ): void { - // Build our SdkConfiguration. - $this->configuration = new SdkConfiguration([ - 'domain' => $env['AUTH0_DOMAIN'] ?? null, - 'clientId' => $env['AUTH0_CLIENT_ID'] ?? null, - 'clientSecret' => $env['AUTH0_CLIENT_SECRET'] ?? null, - 'audience' => ($env['AUTH0_AUDIENCE'] ?? null) !== null ? [trim($env['AUTH0_AUDIENCE'])] : null, - 'organization' => ($env['AUTH0_ORGANIZATION'] ?? null) !== null ? [trim($env['AUTH0_ORGANIZATION'])] : null, - 'strategy' => SdkConfiguration::STRATEGY_API, - ]); - - // Setup the Auth0 SDK. - $this->sdk = new Auth0($this->configuration); - } - - /** - * "Run" our application, responding to end-user requests. - */ - public function run(): void - { - // Intercept exceptions to gracefully report them. - $this->errorHandler->hook(); - - // Handle incoming requests through the router. - $this->router->run(); } /** @@ -141,40 +93,80 @@ public function &getRouter(): ApplicationRouter } /** - * Called from the ApplicationRouter when end user loads '/'. + * Called from the ApplicationRouter when end user loads '/api'. + * + * @param ApplicationRouter $router */ - public function onIndexRoute( - ApplicationRouter $router + public function onApiRoute( + ApplicationRouter $router, ): void { + $session = $this->getToken(); + // Send response to browser. - $this->templates->render('spa', [ - 'config' => $this->getConfiguration(), + $this->templates->render('logged-' . ($session instanceof \Auth0\SDK\Contract\TokenInterface ? 'in' : 'out'), [ + 'session' => $session, 'router' => $router, ]); } /** - * Called from the ApplicationRouter when end user loads '/api'. + * Called from the ApplicationRouter when end user loads an unknown route. + * + * @param ApplicationRouter $router */ - public function onApiRoute( - ApplicationRouter $router + public function onError404( + ApplicationRouter $router, ): void { - $session = $this->getToken(); + $router->setHttpStatus(404); + } + /** + * Called from the ApplicationRouter when end user loads '/'. + * + * @param ApplicationRouter $router + */ + public function onIndexRoute( + ApplicationRouter $router, + ): void { // Send response to browser. - $this->templates->render('logged-' . ($session === null ? 'out' : 'in'), [ - 'session' => $session, + $this->templates->render('spa', [ + 'config' => $this->getConfiguration(), 'router' => $router, ]); } /** - * Called from the ApplicationRouter when end user loads an unknown route. + * "Run" our application, responding to end-user requests. */ - public function onError404( - ApplicationRouter $router + public function run(): void + { + // Intercept exceptions to gracefully report them. + $this->errorHandler->hook(); + + // Handle incoming requests through the router. + $this->router->run(); + } + + /** + * Configure the Auth0 SDK using the .env configuration. + * + * @param array $env Auth0 configuration imported from .env file. + */ + public function setupAuth0( + array $env, ): void { - $router->setHttpStatus(404); + // Build our SdkConfiguration. + $this->configuration = new SdkConfiguration([ + 'domain' => $env['AUTH0_DOMAIN'] ?? null, + 'clientId' => $env['AUTH0_CLIENT_ID'] ?? null, + 'clientSecret' => $env['AUTH0_CLIENT_SECRET'] ?? null, + 'audience' => ($env['AUTH0_AUDIENCE'] ?? null) !== null ? [trim($env['AUTH0_AUDIENCE'])] : null, + 'organization' => ($env['AUTH0_ORGANIZATION'] ?? null) !== null ? [trim($env['AUTH0_ORGANIZATION'])] : null, + 'strategy' => SdkConfiguration::STRATEGY_API, + ]); + + // Setup the Auth0 SDK. + $this->sdk = new Auth0($this->configuration); } /** @@ -188,7 +180,7 @@ private function getToken(): ?\Auth0\SDK\Contract\TokenInterface $token = $_GET['token'] ?? $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['Authorization'] ?? null; // If no token was present, abort processing. - if ($token === null) { + if (null === $token) { return null; } @@ -196,7 +188,7 @@ private function getToken(): ?\Auth0\SDK\Contract\TokenInterface $token = trim($token); // Remove the 'Bearer ' prefix, if present, in the event we're using an Authorization header that's using it. - if (substr($token, 0, 7) === 'Bearer ') { + if (str_starts_with($token, 'Bearer ')) { $token = substr($token, 7); } diff --git a/app/src/ApplicationErrorHandler.php b/app/src/ApplicationErrorHandler.php index c972e27..6bcd52c 100644 --- a/app/src/ApplicationErrorHandler.php +++ b/app/src/ApplicationErrorHandler.php @@ -7,6 +7,8 @@ use Auth0\SDK\Exception\Auth0Exception; use Throwable; +use function array_key_exists; + final class ApplicationErrorHandler { /** @@ -20,9 +22,9 @@ final class ApplicationErrorHandler * @param Application $app An instance of our Quickstart Application. */ public function __construct( - Application &$app + Application &$app, ) { - $this->app = & $app; + $this->app = &$app; } /** @@ -30,7 +32,9 @@ public function __construct( */ public function hook(): void { - set_exception_handler([$this, 'onException']); + set_exception_handler(function (Throwable $throwable): void { + $this->onException($throwable); + }); } /** @@ -39,7 +43,7 @@ public function hook(): void * @param Throwable $throwable The throwable to report. */ public function onException( - \Throwable $throwable + Throwable $throwable, ): void { $exception = $throwable; diff --git a/app/src/ApplicationRouter.php b/app/src/ApplicationRouter.php index 7408a8d..b0a291a 100644 --- a/app/src/ApplicationRouter.php +++ b/app/src/ApplicationRouter.php @@ -4,6 +4,8 @@ namespace Auth0\Quickstart; +use function array_key_exists; + final class ApplicationRouter { /** @@ -17,93 +19,63 @@ final class ApplicationRouter * @param Application $app An instance of our Quickstart Application. */ public function __construct( - Application &$app + Application &$app, ) { - $this->app = & $app; - } - - /** - * Process the current request and route it to the class handler. - * - * @param string $uri The new uri to redirect the end user to. - */ - public function redirect( - string $uri - ): void { - header('Location: ' . $uri, true, 303); - exit; + $this->app = &$app; } /** - * Process the current request and route it to the class handler. + * Return the request method (GET, POST, etc.). */ - public function run(): void + public function getMethod(): string { - $requestUri = parse_url($this->getUri(), PHP_URL_PATH); - - $routed = false; - - if ($requestUri === '/') { - $this->app->onIndexRoute($this); - $routed = true; - } - - if ($requestUri === '/api') { - $this->setHeaders(); - $this->app->onApiRoute($this); - $routed = true; - } - - if ($routed === false) { - $this->setHeaders(); - $this->app->onError404($this); - } - - exit; + return $_SERVER['REQUEST_METHOD'] ?? 'GET'; } /** * Return (and optionally manipulate) the currently requested uri. * - * @param string|null $path Unless null, manipulates the resulting path to match the value. - * @param string|null $query Unless, manipulates the resulting query to match the value. + * @param null|string $path Unless null, manipulates the resulting path to match the value. + * @param null|string $query Unless, manipulates the resulting query to match the value. */ public function getUri( ?string $path = null, - ?string $query = null + ?string $query = null, ): string { $httpScheme = $_SERVER['HTTPS'] ?? ''; - $httpScheme = $httpScheme === 'on' ? 'https' : 'http'; + $httpScheme = 'on' === $httpScheme ? 'https' : 'http'; + $httpPort = (int) $_SERVER['SERVER_PORT']; $httpHost = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME']; $httpHost = preg_replace('/\:' . $httpPort . '$/', '', $httpHost); + $httpRequest = (string) $_SERVER['REQUEST_URI']; - $httpUri = $httpScheme . '://' . $httpHost . ($httpPort !== 80 ? ':' . $httpPort : '') . $httpRequest; + $httpUri = $httpScheme . '://' . $httpHost . (80 !== $httpPort ? ':' . $httpPort : '') . $httpRequest; // If we aren't making changes, simply return the uri. - if ($path === null && $query === null) { + if (null === $path && null === $query) { return $httpUri; } // Parse a url into it's components so we can manipulate them more easily. $parsedUri = parse_url($httpUri); - if ($parsedUri === false) { + if (false === $parsedUri) { return $httpUri; } - $parsedUri['scheme'] = $parsedUri['scheme'] ?? 'http'; - $parsedUri['host'] = $parsedUri['host'] ?? $httpHost; - $parsedUri['path'] = $parsedUri['path'] ?? ''; + $parsedUri['scheme'] ??= 'http'; + $parsedUri['host'] ??= $httpHost; + $parsedUri['path'] ??= ''; $parsedUri['query'] = '?' . ($parsedUri['query'] ?? ''); // Manipulate the /path portion of the uri. - if ($path !== null) { + if (null !== $path) { $parsedUri['path'] = $path; } // Manipulate the ?query portion of the uri. - if ($query !== null) { + if (null !== $query) { $parsedUri['query'] = $query; } @@ -111,24 +83,56 @@ public function getUri( $parsedUri['port'] = 80; } - if ($parsedUri['query'] === '?') { + if ('?' === $parsedUri['query']) { $parsedUri['query'] = ''; } - if ($parsedUri['query'] !== '') { + if ('' !== $parsedUri['query']) { $parsedUri['query'] = '?' . $parsedUri['query']; } // Reconstruct the manipulated uri and return it. - return $parsedUri['scheme'] . '://' . $parsedUri['host'] . ($parsedUri['port'] !== 80 ? ':' . $parsedUri['port'] : '') . $parsedUri['path'] . $parsedUri['query']; + return $parsedUri['scheme'] . '://' . $parsedUri['host'] . (80 !== $parsedUri['port'] ? ':' . $parsedUri['port'] : '') . $parsedUri['path'] . $parsedUri['query']; } /** - * Return the request method (GET, POST, etc.) + * Process the current request and route it to the class handler. + * + * @param string $uri The new uri to redirect the end user to. */ - public function getMethod(): string + public function redirect( + string $uri, + ): void { + header('Location: ' . $uri, true, 303); + exit; + } + + /** + * Process the current request and route it to the class handler. + */ + public function run(): void { - return $_SERVER['REQUEST_METHOD'] ?? 'GET'; + $requestUri = parse_url($this->getUri(), PHP_URL_PATH); + + $routed = false; + + if ('/' === $requestUri) { + $this->app->onIndexRoute($this); + $routed = true; + } + + if ('/api' === $requestUri) { + $this->setHeaders(); + $this->app->onApiRoute($this); + $routed = true; + } + + if (false === $routed) { + $this->setHeaders(); + $this->app->onError404($this); + } + + exit; } /** @@ -151,7 +155,7 @@ public function setHeaders(): void * @param int $status The HTTP status code to send. */ public function setHttpStatus( - int $status + int $status, ): void { http_response_code($status); } diff --git a/app/src/ApplicationTemplates.php b/app/src/ApplicationTemplates.php index dec2cd8..1c17c13 100644 --- a/app/src/ApplicationTemplates.php +++ b/app/src/ApplicationTemplates.php @@ -4,6 +4,12 @@ namespace Auth0\Quickstart; +use Exception; +use LogicException; +use Throwable; + +use function array_key_exists; + final class ApplicationTemplates { /** @@ -14,9 +20,13 @@ final class ApplicationTemplates /** * State machine of the template being rendered. * - * @var array{section: string|null, sections: array, layout: array{name: string, variables: array}|null} + * @var array{section: null|string, sections: array, layout: null|array{name: string, variables: array}} */ - private array $state; + private array $state = [ + 'sections' => [], + 'section' => null, + 'layout' => null, + ]; /** * ApplicationTemplates constructor. @@ -24,26 +34,20 @@ final class ApplicationTemplates * @param Application $app An instance of our Quickstart Application. */ public function __construct( - Application &$app + Application &$app, ) { - $this->app = & $app; - - $this->state = [ - 'sections' => [], - 'section' => null, - 'layout' => null, - ]; + $this->app = &$app; } /** * Render a template as the browser response, then exit. * - * @param string $template The name of the template to use. + * @param string $template The name of the template to use. * @param array $variables Any variables the template should have access to use. */ public function render( string $template, - array $variables = [] + array $variables = [], ): void { $this->state = [ 'sections' => [], @@ -59,6 +63,22 @@ public function render( exit; } + /** + * Define a container layout in which to render a template. + * + * @param string $name The name of the layout template to use. + * @param array $variables Any additional variables the layout template should have access to use. + */ + private function layout( + string $name, + array $variables = [], + ): void { + $this->state['layout'] = [ + 'name' => $name, + 'variables' => $variables, + ]; + } + /** * Render a template, and return the content as a string. * @@ -67,19 +87,19 @@ public function render( */ private function renderTemplate( string $template, - array $variables = [] + array $variables = [], ): string { // Keep track of the output buffering 'level'. $level = 0; // Resolve the requested template to it's file path: - $templatePath = join(DIRECTORY_SEPARATOR, [APP_ROOT, 'templates', $template . '.php']); + $templatePath = implode(DIRECTORY_SEPARATOR, [APP_ROOT, 'templates', $template . '.php']); // Extract $variables into current scope, for use in template. extract($variables); - if (file_exists($templatePath) === false) { - throw new \Exception("Template file not found: {$template}"); + if (! file_exists($templatePath)) { + throw new Exception(sprintf('Template file not found: %s', $template)); } try { @@ -90,7 +110,7 @@ private function renderTemplate( $content = ob_get_clean(); - if ($this->state['layout'] !== null) { + if (null !== $this->state['layout']) { $layoutTemplate = $this->state['layout']['name']; $layoutVariables = array_merge($variables, $this->state['layout']['variables']); @@ -100,17 +120,17 @@ private function renderTemplate( $content = $this->renderTemplate($layoutTemplate, $layoutVariables); } - if ($content !== false) { + if (false !== $content) { return trim($content); } return ''; - } catch (\Throwable $e) { + } catch (Throwable $throwable) { while (ob_get_level() > $level) { ob_end_clean(); } - throw $e; + throw $throwable; } } @@ -120,7 +140,7 @@ private function renderTemplate( * @param string $sectionName Name of the section to render into the template. */ private function section( - string $sectionName + string $sectionName, ): string { return $this->state['sections'][$sectionName] ?? ''; } @@ -131,10 +151,10 @@ private function section( * @param string $sectionName Name of the section to begin capturing. */ private function start( - string $sectionName + string $sectionName, ): void { - if ($this->state['section'] !== null) { - throw new \LogicException('Nested sections are not supported.'); + if (null !== $this->state['section']) { + throw new LogicException('Nested sections are not supported.'); } $this->state['section'] = $sectionName; @@ -147,8 +167,8 @@ private function start( */ private function stop(): void { - if ($this->state['section'] === null) { - throw new \LogicException('You must start a section before stopping it.'); + if (null === $this->state['section']) { + throw new LogicException('You must start a section before stopping it.'); } if (array_key_exists($this->state['section'], $this->state['sections'])) { @@ -158,20 +178,4 @@ private function stop(): void $this->state['sections'][$this->state['section']] = ob_get_clean(); $this->state['section'] = null; } - - /** - * Define a container layout in which to render a template. - * - * @param string $name The name of the layout template to use. - * @param array $variables Any additional variables the layout template should have access to use. - */ - private function layout( - string $name, - array $variables = [] - ): void { - $this->state['layout'] = [ - 'name' => $name, - 'variables' => $variables, - ]; - } } diff --git a/app/tests/Feature/ExampleTest.php b/app/tests/Feature/ExampleTest.php new file mode 100644 index 0000000..61cd84c --- /dev/null +++ b/app/tests/Feature/ExampleTest.php @@ -0,0 +1,5 @@ +toBeTrue(); +}); diff --git a/app/tests/Pest.php b/app/tests/Pest.php new file mode 100644 index 0000000..5949c61 --- /dev/null +++ b/app/tests/Pest.php @@ -0,0 +1,45 @@ +in('Feature'); + +/* +|-------------------------------------------------------------------------- +| Expectations +|-------------------------------------------------------------------------- +| +| When you're writing tests, you often need to check that values meet certain conditions. The +| "expect()" function gives you access to a set of "expectations" methods that you can use +| to assert different things. Of course, you may extend the Expectation API at any time. +| +*/ + +expect()->extend('toBeOne', function () { + return $this->toBe(1); +}); + +/* +|-------------------------------------------------------------------------- +| Functions +|-------------------------------------------------------------------------- +| +| While Pest is very powerful out-of-the-box, you may have some testing code specific to your +| project that you don't want to repeat in every file. Here you can also expose helpers as +| global functions to help you to reduce the number of lines of code in your test files. +| +*/ + +function something() +{ + // .. +} diff --git a/app/tests/TestCase.php b/app/tests/TestCase.php new file mode 100644 index 0000000..cfb05b6 --- /dev/null +++ b/app/tests/TestCase.php @@ -0,0 +1,10 @@ +toBeTrue(); +});