diff --git a/CHANGELOG-5.0.md b/CHANGELOG-5.0.md index bb0247fe60..aa3db0cb41 100644 --- a/CHANGELOG-5.0.md +++ b/CHANGELOG-5.0.md @@ -7,7 +7,7 @@ - Changed `Phalcon\Filter\Validation\Validator\Email` to allow UTF8 in local part. [#16637](https://github.com/phalcon/cphalcon/issues/16637) ### Added - +- Added `dispatch:beforeCallAction` and `dispatch:afterCallAction` to last-minute modifications to handler and method (mostly for debugging). ### Fixed @@ -17,6 +17,7 @@ - Fixed `Phalcon\Filter\Validation\Validator\File\MimeType::validate` to close the handle when using `finfo` [#16647](https://github.com/phalcon/cphalcon/issues/16647) - Fixed `Phalcon\Mvc\Model\Manager::getRelationRecords` to explicitly set the `referencedModel` in the conditions along with the `referencedFields` [#16655](https://github.com/phalcon/cphalcon/pull/16655) - Fixed `Phalcon\Image\Adapters\AbstractAdapter::watermark` to correctly calculate the Y offset [#16658](https://github.com/phalcon/cphalcon/issues/16658) +- Fixed `Phalcon\Dispatcher\AbstractDispatcher` when calling action methods that do not define parameters to prevent `Unknown named parameter` error. ### Removed diff --git a/phalcon/Dispatcher/AbstractDispatcher.zep b/phalcon/Dispatcher/AbstractDispatcher.zep index 296a6c2a6c..d329d17ba5 100644 --- a/phalcon/Dispatcher/AbstractDispatcher.zep +++ b/phalcon/Dispatcher/AbstractDispatcher.zep @@ -19,6 +19,7 @@ use Phalcon\Events\ManagerInterface; use Phalcon\Filter\FilterInterface; use Phalcon\Mvc\Model\Binder; use Phalcon\Mvc\Model\BinderInterface; +use Phalcon\Support\Collection; /** * This is the base class for Phalcon\Mvc\Dispatcher and Phalcon\Cli\Dispatcher. @@ -154,10 +155,52 @@ abstract class AbstractDispatcher extends AbstractInjectionAware implements Disp public function callActionMethod(handler, string actionMethod, array! params = []) { - return call_user_func_array( - [handler, actionMethod], - params + var result, observer, altHandler, altAction, altParams; + + let altHandler = handler; + let altAction = actionMethod; + let altParams = params; + + if this->eventsManager !== null && this->eventsManager instanceof ManagerInterface { + let observer = this->getDi()->get( + "Phalcon\Support\Collection", + [[ + "handler": handler, + "action": actionMethod, + "params": params + ]] + ); + + this->eventsManager->fire( + "dispatch:beforeCallAction", + this, + observer + ); + + let altHandler = observer->get("handler"); + let altAction = observer->get("action"); + let altParams = observer->get("params", [], "array"); + } + + let result = call_user_func_array( + [ + altHandler, + altAction + ], + array_values(altParams) ); + + if this->eventsManager !== null && this->eventsManager instanceof ManagerInterface { + let observer["result"] = result; + + this->eventsManager->fire( + "dispatch:afterCallAction", + this, + observer + ); + } + + return result; } /** diff --git a/tests/unit/Dispatcher/CallActionMethodCest.php b/tests/unit/Dispatcher/CallActionMethodCest.php new file mode 100644 index 0000000000..2754b0ab83 --- /dev/null +++ b/tests/unit/Dispatcher/CallActionMethodCest.php @@ -0,0 +1,139 @@ + + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Tests\Unit\Dispatcher; + +use Phalcon\Di\Di; +use Phalcon\Events\Event; +use Phalcon\Events\Manager; +use Phalcon\Mvc\Dispatcher; +use Phalcon\Support\Collection; +use UnitTester; + +class CallActionMethodCest +{ + private bool $wasCalled = false; + private bool $altCalled = false; + private string $paramCalled = ''; + + public function _before(UnitTester $I) + { + $this->wasCalled = false; + $this->altCalled = false; + $this->paramCalled = ''; + } + + /** + * Tests Phalcon\Dispatcher :: callActionMethod() + * + * @author Phalcon Team + * @since 2025-01-06 + */ + public function dispatcherCallActionMethod(UnitTester $I) + { + $I->wantToTest('Dispatcher - callActionMethod()'); + + $dispatcher = new Dispatcher(); + + $dispatcher->callActionMethod( + $this, + '_wasCalled' + ); + + $I->assertTrue($this->wasCalled); + $I->assertFalse($this->altCalled); + } + + /** + * Tests Phalcon\Dispatcher :: callActionMethod() + * + * @author Phalcon Team + * @since 2025-01-06 + */ + public function dispatcherCallActionMethodWithParams(UnitTester $I) + { + $I->wantToTest('Dispatcher - callActionMethod() - Params'); + + $dispatcher = new Dispatcher(); + + $dispatcher->callActionMethod( + $this, + '_paramCalled', + [ + 'something' => 'else' + ] + ); + + $I->assertFalse($this->wasCalled); + $I->assertFalse($this->altCalled); + $I->assertEquals('else', $this->paramCalled); + + $dispatcher->callActionMethod( + $this, + '_paramCalled', + [ + 'something' + ] + ); + + $I->assertFalse($this->wasCalled); + $I->assertFalse($this->altCalled); + $I->assertEquals('something', $this->paramCalled); + } + + /** + * Tests Phalcon\Dispatcher :: callActionMethod() Events + * + * @author Phalcon Team + * @since 2025-01-06 + */ + public function dispatcherCallActionMethodWithEvents(UnitTester $I) + { + $I->wantToTest('Dispatcher - callActionMethod() - Events'); + + $eventsManager = new Manager(); + $eventsManager->attach( + 'dispatch:beforeCallAction', + function (Event $event, Dispatcher $dispatcher, Collection $observer) { + $observer->action = "_altCalled"; + } + ); + + $dispatcher = new Dispatcher(); + $dispatcher->setEventsManager($eventsManager); + $dispatcher->setDi(new Di()); + + $dispatcher->callActionMethod( + $this, + '_wasCalled' + ); + + $I->assertTrue($this->altCalled); + $I->assertFalse($this->wasCalled); + } + + public function _wasCalled(): void + { + $this->wasCalled = true; + } + + public function _altCalled(): void + { + $this->altCalled = true; + } + + public function _paramCalled(string $param): void + { + $this->paramCalled = $param; + } +}