Skip to content

Commit

Permalink
Merge branch 'refs/heads/4.x' into development
Browse files Browse the repository at this point in the history
# Conflicts:
#	tests/RouteTest.php
  • Loading branch information
natanfelles committed Jul 23, 2024
2 parents 368b774 + c514e88 commit 6881992
Show file tree
Hide file tree
Showing 13 changed files with 160 additions and 57 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-22.04
timeout-minutes: 10

name: PHP 8.1
name: PHP 8.3

steps:
- name: Checkout
Expand All @@ -20,7 +20,7 @@ jobs:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.1
php-version: 8.3
tools: composer
coverage: xdebug

Expand Down
2 changes: 1 addition & 1 deletion .gitlab-ci.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
image: registry.gitlab.com/aplus-framework/images/base:2
image: registry.gitlab.com/aplus-framework/images/base:4

include:
- template: Security/SAST.gitlab-ci.yml
Expand Down
8 changes: 4 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@
}
],
"require": {
"php": ">=8.1",
"aplus/debug": "^3.1",
"aplus/http": "^5.0",
"aplus/language": "^3.0"
"php": ">=8.3",
"aplus/debug": "^4.0",
"aplus/http": "^6.0",
"aplus/language": "^4.0"
},
"require-dev": {
"ext-xdebug": "*",
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: "3"
services:
package:
image: registry.gitlab.com/aplus-framework/images/package:2
image: registry.gitlab.com/aplus-framework/images/package:4
container_name: package-routing
working_dir: /package
volumes:
Expand Down
27 changes: 14 additions & 13 deletions guide/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -160,22 +160,23 @@ Actions can also be defined as strings. Following the format below:
The Class must extend the **Framework\Routing\RouteActions** class and have
the action method.

The arguments are numbers separated by slashes after the method name.
The arguments start with the dollar sign and then have numbers that are
separated by slashes after the method name.

The number of arguments starts at zero and can have custom order. These are the
arguments that will go to the action class method.

.. code-block:: php
$routes->get('/posts/{int}', 'Posts::show/0');
$routes->get('/posts/{int}', 'Posts::show/$0');
Let's see an example creating the ``Posts`` class, which will have the ``show``
method, which will receive two arguments. In the first will be the value of
placeholder ``{int}`` and in the second will be the value of ``{slug}``:

.. code-block:: php
$routes->get('/categories/{slug}/posts/{int}/', 'Posts::show/1/0');
$routes->get('/categories/{slug}/posts/{int}/', 'Posts::show/$1/$0');
Let's see the class that serves this route:

Expand All @@ -194,7 +195,7 @@ Let's see the class that serves this route:
public function show(int $id, string $category)
{
echo 'Category slug is: ' . $categoryId;
echo 'Category slug is: ' . $category;
echo 'Post id is: ' . $id;
var_dump($this->constructorArguments);
}
Expand Down Expand Up @@ -263,8 +264,8 @@ collection is accepted, which will be prefixed to the name of the routes:
// Different HTTP Methods using placeholders
$routes->get('/user', 'App\Users::index');
$routes->post('/user', 'App\Users::create');
$routes->get('/user/{int}', 'App\Users::show/0');
$routes->patch('/user/{username}', 'App\Users::update/0');
$routes->get('/user/{int}', 'App\Users::show/$0');
$routes->patch('/user/{username}', 'App\Users::update/$0');
$routes->put('/user/{int}', [\App\Users::class, 'replace']);
$routes->delete('/user/{int}', 'App\Users::delete/*');
Expand Down Expand Up @@ -303,7 +304,7 @@ Which will create 6 routes, as follows:
| **DELETE** | /users/{int} | App\Users::delete/* | users.delete |
+-----------------+--------------+----------------------+---------------+

In the fourth parameter of the ``serve`` method it is possible to be in an array
In the fourth parameter of the ``resource`` method it is possible to be in an array
the routes that should not be added. And they are: ``index``, ``create``, ``show``,
``update``, ``replace`` and ``delete``.

Expand Down Expand Up @@ -362,7 +363,7 @@ is possible to group them with a base path.
// Route for "/blog/"
$routes->get('/', 'App\Blog\Posts::index'),
// Route for "/blog/{title}"
$routes->get('/{title}', 'App\Blog\Posts::show/0'),
$routes->get('/{title}', 'App\Blog\Posts::show/$0'),
]);
Grouping works on multiple layers. This also works:
Expand All @@ -376,7 +377,7 @@ Grouping works on multiple layers. This also works:
// Route for "/blog/posts/"
$routes->get('/', 'App\Blog\Posts::index'),
// Route for "/blog/posts/{title}"
$routes->get('/{title}','App\Blog\Posts::show/0'),
$routes->get('/{title}','App\Blog\Posts::show/$0'),
]),
]);
Expand All @@ -394,8 +395,8 @@ It is possible group route actions with the ``namespace`` method:
$routes->group('/blog', [
// Routes "/blog/posts" for App\Controllers\Blog\Posts::index
$routes->get('/posts', 'Posts::index'),
// Routes "/blog/posts/{title}" for App\Controllers\Blog\Posts::show/0
$routes->get('/posts/{title}', 'Posts::show/0'),
// Routes "/blog/posts/{title}" for App\Controllers\Blog\Posts::show/$0
$routes->get('/posts/{title}', 'Posts::show/$0'),
]),
]),
]);
Expand Down Expand Up @@ -465,7 +466,7 @@ action and redirect to the route named ``access.login``:
protected function beforeAction(string $method, array $arguments) : mixed
{
if( ! isset($_SESSION['user_id'])) {
if(!isset($_SESSION['user_id'])) {
return $this->response->redirect(
$this->router->getNamedRoute('access.login')->getUrl()
);
Expand All @@ -490,7 +491,7 @@ action and redirect to the route named ``access.login``:
$router->serve(null, function(RouteCollection $routes) {
$routes->get('admin', 'Admin::index');
$routes->get('foo/other', 'Admin::something/1/0');
$routes->get('foo/other', 'Admin::something/$1/$0');
$routes->get('login', 'Access\Login::index', 'access.login');
});
Expand Down
22 changes: 22 additions & 0 deletions src/Debug/RoutingCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php declare(strict_types=1);
/*
* This file is part of Aplus Framework Routing Library.
*
* (c) Natan Felles <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Framework\Routing\Debug;

use Framework\Debug\Collection;

/**
* Class RoutingCollection.
*
* @package routing
*/
class RoutingCollection extends Collection
{
protected string $iconPath = __DIR__ . '/icons/routing.svg';
}
1 change: 1 addition & 0 deletions src/Debug/icons/routing.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
45 changes: 24 additions & 21 deletions src/Route.php
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ public function getActionArguments() : array
public function setActionArguments(array $arguments) : static
{
\ksort($arguments);
/*foreach ($arguments as $i => $argument) {
$this->actionArguments[++$i] = $argument;
}*/
$this->actionArguments = $arguments;
return $this;
}
Expand Down Expand Up @@ -281,7 +284,7 @@ public function run(mixed ...$construct) : Response
[$classname, $action] = \explode('::', $action, 2);
[$method, $arguments] = $this->extractMethodAndArguments($action);
if (!\class_exists($classname)) {
throw new RoutingException("Class not exists: {$classname}");
throw new RoutingException("Class does not exist: {$classname}");
}
/**
* @var RouteActions $class
Expand All @@ -294,7 +297,7 @@ public function run(mixed ...$construct) : Response
}
if (!\method_exists($class, $method)) {
throw new RoutingException(
"Class action method not exists: {$classname}::{$method}"
"Class action method does not exist: {$classname}::{$method}"
);
}
$result = $class->beforeAction($method, $arguments); // @phpstan-ignore-line
Expand Down Expand Up @@ -344,13 +347,6 @@ protected function makeResponseBodyPart(mixed $result) : string
if (\is_scalar($result)) {
return (string) $result;
}
if (\is_object($result) && \method_exists($result, '__toString')) {
\trigger_error(
'Object to string conversion is deprecated',
\E_USER_DEPRECATED
);
return (string) $result;
}
if (
\is_array($result)
|| $result instanceof \stdClass
Expand All @@ -359,6 +355,9 @@ protected function makeResponseBodyPart(mixed $result) : string
$this->router->getResponse()->setJson($result);
return '';
}
if (\is_object($result) && \method_exists($result, '__toString')) {
return (string) $result;
}
$type = \get_debug_type($result);
throw new RoutingException(
"Invalid action return type '{$type}'" . $this->onNamedRoutePart()
Expand All @@ -380,29 +379,33 @@ protected function extractMethodAndArguments(
if (!\str_contains($part, '/')) {
return [$part, []];
}
$part = \rtrim($part, '/');
$arguments = \explode('/', $part);
$method = $arguments[0];
unset($arguments[0]);
$actionArguments = $this->getActionArguments();
$arguments = \array_values($arguments);
foreach ($arguments as $index => $arg) {
if (\is_numeric($arg)) {
$arg = (int) $arg;
if (\array_key_exists($arg, $actionArguments)) {
$arguments[$index] = $actionArguments[$arg];
continue;
if ($arg[0] === '$') {
$arg = \substr($arg, 1);
if (\is_numeric($arg) /*&& $arg > 0*/) {
$arg = (int) $arg;
if (\array_key_exists($arg, $actionArguments)) {
$arguments[$index] = $actionArguments[$arg];
continue;
}
throw new InvalidArgumentException(
"Undefined action argument: \${$arg}" . $this->onNamedRoutePart()
);
}
throw new InvalidArgumentException(
"Undefined action argument: {$arg}" . $this->onNamedRoutePart()
"Invalid action argument: \${$arg}" . $this->onNamedRoutePart()
);
}
if ($arg !== '*') {
throw new InvalidArgumentException(
'Action argument is not numeric, or has not an allowed wildcard, on index ' . $index
. $this->onNamedRoutePart()
);
$arguments[$index] = $arg;
continue;
}
if ($index !== 0 || \count($arguments) > 1) {
if ($index > 1 || \count($arguments) > 1) {
throw new InvalidArgumentException(
'Action arguments can only contain an asterisk wildcard and must be passed alone'
. $this->onNamedRoutePart()
Expand Down
20 changes: 15 additions & 5 deletions src/RouteCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Closure;
use Error;
use Framework\HTTP\Method;
use Framework\HTTP\Status;
use InvalidArgumentException;
use LogicException;

Expand Down Expand Up @@ -412,19 +413,28 @@ public function options(
*
* @param string $path The URL path
* @param string $location The URL to redirect
* @param int|null $code The status code of the response
* @param int $code The status code of the response
* @param string|null $name The Route name
*
* @return Route The Route added to the collection
*/
public function redirect(string $path, string $location, int $code = null) : Route
{
public function redirect(
string $path,
string $location,
int $code = Status::TEMPORARY_REDIRECT,
string $name = null
) : Route {
$response = $this->router->getResponse();
return $this->addSimple(
'GET',
$path,
static function () use ($response, $location, $code) : void {
static function (array $args) use ($response, $location, $code) : void {
foreach ($args as $key => $value) {
$location = \strtr($location, ['$' . $key => $value]);
}
$response->redirect($location, [], $code);
}
},
$name
);
}

Expand Down
28 changes: 28 additions & 0 deletions tests/Debug/RoutingCollectionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php
/*
* This file is part of Aplus Framework Routing Library.
*
* (c) Natan Felles <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Tests\Routing\Debug;

use Framework\Routing\Debug\RoutingCollection;
use PHPUnit\Framework\TestCase;

final class RoutingCollectionTest extends TestCase
{
protected RoutingCollection $collection;

protected function setUp() : void
{
$this->collection = new RoutingCollection('Routing');
}

public function testIcon() : void
{
self::assertStringStartsWith('<svg ', $this->collection->getIcon());
}
}
19 changes: 19 additions & 0 deletions tests/RouteCollectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,25 @@ public function testRedirect() : void
self::assertSame('Temporary Redirect', $response->getStatusReason());
}

public function testRedirectWithPlaceholders() : void
{
$response = $this->router->getResponse();
$route = $this->collection->redirect(
'/blog/{int}/comments/{int}',
'/blog/posts/$1/foo/$0'
);
$route->setActionArguments([
'25',
'10',
])->run();
self::assertSame(
'/blog/posts/10/foo/25',
$response->getHeader('Location')
);
self::assertSame(307, $response->getStatusCode());
self::assertSame('Temporary Redirect', $response->getStatusReason());
}

public function testMethodNotAllowed() : void
{
$this->expectException(\BadMethodCallException::class);
Expand Down
Loading

0 comments on commit 6881992

Please sign in to comment.