diff --git a/.gitattributes b/.gitattributes index f87c6cd..2d50cdb 100644 --- a/.gitattributes +++ b/.gitattributes @@ -10,3 +10,6 @@ phpunit.xml.dist export-ignore psalm.xml export-ignore psalm-baseline.xml export-ignore .readthedocs.yml export-ignore +docs export-ignore +tests export-ignore +CONTRIBUTING.md export-ignore diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bcd0126..a6b864c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,33 +1,17 @@ name: CI -on: [push, pull_request] +on: + push: + branches: + - master + pull_request: + branches: + - '*' + +permissions: + contents: read jobs: cs-stan: - name: Coding Standard & Static Analysis - runs-on: ubuntu-22.04 - - steps: - - uses: actions/checkout@v3 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: '7.4' - extensions: mbstring, intl - coverage: none - tools: phpstan:1, vimeo/psalm:4 - - - name: Composer Install - run: composer require --dev cakephp/cakephp-codesniffer:^4.0 - - - name: Run PHP CodeSniffer - run: vendor/bin/phpcs --standard=vendor/cakephp/cakephp-codesniffer/CakePHP -p src/ tests/ - - - name: Run psalm - if: always() - run: psalm --output-format=github - - - name: Run phpstan - if: always() - run: phpstan + uses: ADmad/.github/.github/workflows/cs-stan.yml@master + secrets: inherit diff --git a/.gitignore b/.gitignore index e96516b..cf1045a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ composer.lock vendor .phpunit.result.cache +.phpunit.cache +phpstan.neon +phpunit.xml.dist +psalm.xml diff --git a/composer.json b/composer.json index 62876a4..dd768e7 100644 --- a/composer.json +++ b/composer.json @@ -22,12 +22,12 @@ } ], "require":{ - "cakephp/cakephp":"^4.0", - "friendsofcake/crud":"^6.0" + "cakephp/cakephp":"^5.0", + "friendsofcake/crud":"^7.0" }, "require-dev":{ - "phpunit/phpunit":"^8.5 || ^9.3", - "friendsofcake/cakephp-test-utilities":"^2.0" + "phpunit/phpunit":"^10.1 || ^11.0", + "friendsofcake/cakephp-test-utilities":"^3.0" }, "autoload":{ "psr-4":{ diff --git a/docs/_partials/actions/configuration/enabled.rst b/docs/_partials/actions/configuration/enabled.rst index 19833f1..3094f3e 100644 --- a/docs/_partials/actions/configuration/enabled.rst +++ b/docs/_partials/actions/configuration/enabled.rst @@ -4,7 +4,7 @@ enabled Test or modify if the Crud Action is enabled or not. When a CrudAction is disabled, Crud will not handle any requests to the action, and CakePHP will raise the normal -``\Cake\Error\MissingActionException`` exception if you haven't implemented the action in your controller. +``\Cake\Controller\Exception\MissingActionException`` exception if you haven't implemented the action in your controller. .. warning:: diff --git a/docs/_partials/events/after_find.rst b/docs/_partials/events/after_find.rst index d141c2f..7d52929 100644 --- a/docs/_partials/events/after_find.rst +++ b/docs/_partials/events/after_find.rst @@ -20,7 +20,7 @@ Logging the Found Item public function delete($id) { $this->Crud->on('afterFind', function(\Cake\Event\EventInterface $event) { - $this->log("Found item: " . $event->subject()->entity->id . " in the database"); + $this->log("Found item: " . $event->getSubject()->entity->id . " in the database"); }); return $this->Crud->execute(); diff --git a/docs/_partials/events/after_save.rst b/docs/_partials/events/after_save.rst index a5188b5..57499ed 100644 --- a/docs/_partials/events/after_save.rst +++ b/docs/_partials/events/after_save.rst @@ -22,7 +22,7 @@ Check Created Status public function edit($id) { $this->Crud->on('afterSave', function(\Cake\Event\EventInterface $event) { - if ($event->subject()->created) { + if ($event->getSubject()->created) { $this->log("The entity was created"); } else { $this->log("The entity was updated"); @@ -40,7 +40,7 @@ Check Success Status public function edit($id) { $this->Crud->on('afterSave', function(\Cake\Event\EventInterface $event) { - if ($event->subject()->success) { + if ($event->getSubject()->success) { $this->log("The entity was saved successfully"); } else { $this->log("The entity was NOT saved successfully"); @@ -58,8 +58,8 @@ Get Entity ID public function add() { $this->Crud->on('afterSave', function(\Cake\Event\EventInterface $event) { - if ($event->subject()->created) { - $this->log("The entity was created with id: " . $event->subject()->id); + if ($event->getSubject()->created) { + $this->log("The entity was created with id: " . $event->getSubject()->id); } }); diff --git a/docs/_partials/events/before_find.rst b/docs/_partials/events/before_find.rst index ff42531..f325deb 100644 --- a/docs/_partials/events/before_find.rst +++ b/docs/_partials/events/before_find.rst @@ -34,7 +34,7 @@ Add Conditions public function delete($id) { $this->Crud->on('beforeFind', function(\Cake\Event\EventInterface $event) { - $event->subject()->query->where(['author' => $this->Authentication->getIdentityData('id')]); + $event->getSubject()->query->where(['author' => $this->Authentication->getIdentityData('id')]); }); return $this->Crud->execute(); diff --git a/docs/_partials/events/set_flash.rst b/docs/_partials/events/set_flash.rst index 4bdda42..f9d25c8 100644 --- a/docs/_partials/events/set_flash.rst +++ b/docs/_partials/events/set_flash.rst @@ -48,8 +48,8 @@ If you'd like to configure it on the fly you can use the eventManager to change .. code-block:: phpinline $this->eventManager()->on('Crud.setFlash', function (\Cake\Event\EventInterface $event) { - if ($event->subject()->success) { - $event->subject()->params['class'] = 'alert alert-success alert-dismissible'; + if ($event->getSubject()->success) { + $event->getSubject()->params['class'] = 'alert alert-success alert-dismissible'; } }); diff --git a/docs/installation.rst b/docs/installation.rst index 527f3a3..cf0e142 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -3,12 +3,6 @@ Installation Installing CRUD Users requires only a few steps -Requirements ------------- - -* CakePHP 3.x -* PHP 5.6+ - Getting the Code ---------------- diff --git a/phpstan.neon b/phpstan.neon index bace0ac..30c5046 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,11 +1,12 @@ parameters: level: 8 - checkMissingIterableValueType: false paths: - src universalObjectCratesClasses: - Crud\Event\Subject ignoreErrors: + - + identifier: missingType.iterableValue - message: "#^Access to an undefined property Cake\\\\Controller\\\\Controller\\:\\:\\$Authentication\\.$#" count: 4 diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 76ddaee..fc39c42 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -2,9 +2,8 @@ - - + ./tests/ diff --git a/psalm.xml b/psalm.xml index 09cd5b1..44f7784 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,10 +1,13 @@ diff --git a/src/Action/ForgotPasswordAction.php b/src/Action/ForgotPasswordAction.php index 8188997..036b5a6 100644 --- a/src/Action/ForgotPasswordAction.php +++ b/src/Action/ForgotPasswordAction.php @@ -18,7 +18,7 @@ class ForgotPasswordAction extends BaseAction use ViewTrait; use ViewVarTrait; - protected $_defaultConfig = [ + protected array $_defaultConfig = [ 'enabled' => true, 'scope' => 'entity', 'findMethod' => 'all', @@ -57,9 +57,9 @@ protected function _get(): void /** * HTTP POST handler * - * @return \Cake\Http\Response|null|void + * @return \Cake\Http\Response|null */ - protected function _post() + protected function _post(): ?Response { $subject = $this->_subject([ 'findMethod' => $this->_getFindConfig(), @@ -67,14 +67,14 @@ protected function _post() $this->_trigger('beforeForgotPassword', $subject); - $entity = $this->_table() - ->find($subject->findMethod[0], $subject->findMethod[1]) + $entity = $this->_model() + ->find($subject->findMethod[0], ...$subject->findMethod[1]) ->first(); if (empty($entity)) { $this->_error($subject); - return; + return null; } $subject->set(['entity' => $entity]); diff --git a/src/Action/LoginAction.php b/src/Action/LoginAction.php index 81b3543..9fa0928 100644 --- a/src/Action/LoginAction.php +++ b/src/Action/LoginAction.php @@ -12,7 +12,7 @@ class LoginAction extends BaseAction { use RedirectTrait; - protected $_defaultConfig = [ + protected array $_defaultConfig = [ 'enabled' => true, 'messages' => [ 'success' => [ @@ -28,10 +28,11 @@ class LoginAction extends BaseAction /** * HTTP GET handler * - * @return \Cake\Http\Response|null|void + * @return \Cake\Http\Response|null */ - protected function _get() + protected function _get(): ?Response { + /** @psalm-suppress UndefinedMagicPropertyFetch */ $result = $this->_controller()->Authentication->getResult(); $subject = $this->_subject([ 'success' => true, @@ -43,15 +44,18 @@ protected function _get() } $this->_trigger('beforeRender', $subject); + + return null; } /** * HTTP POST handler * - * @return \Cake\Http\Response|null|void + * @return \Cake\Http\Response|null */ - protected function _post() + protected function _post(): ?Response { + /** @psalm-suppress UndefinedMagicPropertyFetch */ $result = $this->_controller()->Authentication->getResult(); $subject = $this->_subject([ 'result' => $result, @@ -62,6 +66,8 @@ protected function _post() } $this->_error($subject); + + return null; } /** @@ -72,6 +78,7 @@ protected function _post() */ protected function _success(Subject $subject): ?Response { + /** @psalm-suppress UndefinedMagicPropertyFetch */ $subject->set([ 'success' => true, 'identity' => $this->_controller()->Authentication->getIdentity(), diff --git a/src/Action/LogoutAction.php b/src/Action/LogoutAction.php index 469e6f0..e723072 100644 --- a/src/Action/LogoutAction.php +++ b/src/Action/LogoutAction.php @@ -3,6 +3,7 @@ namespace CrudUsers\Action; +use Cake\Http\Response; use Crud\Action\BaseAction; use Crud\Traits\RedirectTrait; @@ -10,7 +11,7 @@ class LogoutAction extends BaseAction { use RedirectTrait; - protected $_defaultConfig = [ + protected array $_defaultConfig = [ 'enabled' => true, 'messages' => [ 'success' => [ @@ -25,11 +26,12 @@ class LogoutAction extends BaseAction * * @return \Cake\Http\Response|null */ - protected function _get() + protected function _get(): ?Response { $subject = $this->_subject(); $this->_trigger('beforeLogout', $subject); + /** @psalm-suppress UndefinedMagicPropertyFetch */ $redirectUrl = $this->_controller()->Authentication->logout(); $redirectUrl = $this->getConfig('redirectUrl', $redirectUrl); if ($redirectUrl === false) { diff --git a/src/Action/RegisterAction.php b/src/Action/RegisterAction.php index 23991b9..b444351 100644 --- a/src/Action/RegisterAction.php +++ b/src/Action/RegisterAction.php @@ -19,7 +19,7 @@ class RegisterAction extends BaseAction use ViewTrait; use ViewVarTrait; - protected $_defaultConfig = [ + protected array $_defaultConfig = [ 'enabled' => true, 'scope' => 'entity', 'inflection' => 'singular', @@ -60,7 +60,7 @@ class RegisterAction extends BaseAction * * @return void */ - protected function _get() + protected function _get(): void { $subject = $this->_subject([ 'success' => true, @@ -76,9 +76,9 @@ protected function _get() /** * HTTP POST handler * - * @return \Cake\Http\Response|null|void + * @return \Cake\Http\Response|null */ - protected function _post() + protected function _post(): ?Response { $subject = $this->_subject([ 'entity' => $this->_entity($this->_request()->getData(), $this->saveOptions()), @@ -89,12 +89,14 @@ protected function _post() $this->_trigger('beforeRegister', $subject); /** @var callable $callback */ - $callback = [$this->_table(), $subject->saveMethod]; + $callback = [$this->_model(), $subject->saveMethod]; if ($callback($subject->entity, $subject->saveOptions)) { return $this->_success($subject); } $this->_error($subject); + + return null; } /** diff --git a/src/Action/ResetPasswordAction.php b/src/Action/ResetPasswordAction.php index 5deb13c..9712bf4 100644 --- a/src/Action/ResetPasswordAction.php +++ b/src/Action/ResetPasswordAction.php @@ -3,6 +3,7 @@ namespace CrudUsers\Action; +use Cake\Datasource\EntityInterface; use Cake\Http\Exception\BadRequestException; use Cake\Http\Exception\NotFoundException; use Cake\Http\Response; @@ -25,7 +26,7 @@ class ResetPasswordAction extends BaseAction use ViewTrait; use ViewVarTrait; - protected $_defaultConfig = [ + protected array $_defaultConfig = [ 'enabled' => true, 'scope' => 'entity', 'findMethod' => 'all', @@ -79,7 +80,7 @@ protected function _get(?string $token = null): void $subject = $this->_subject([ 'success' => true, - 'entity' => $this->_table()->newEmptyEntity(), + 'entity' => $this->_model()->newEmptyEntity(), 'token' => $token, ]); @@ -92,9 +93,9 @@ protected function _get(?string $token = null): void * Thin proxy for _put * * @param string|null $token Token - * @return \Cake\Http\Response|null|void + * @return \Cake\Http\Response|null */ - protected function _post($token = null) + protected function _post(?string $token = null): ?Response { return $this->_put($token); } @@ -103,9 +104,9 @@ protected function _post($token = null) * HTTP PUT handler * * @param string|null $token Token - * @return \Cake\Http\Response|null|void + * @return \Cake\Http\Response|null */ - protected function _put($token = null) + protected function _put(?string $token = null): ?Response { $entity = $this->_verify($this->_token($token)); @@ -115,6 +116,8 @@ protected function _put($token = null) } $this->_error($subject); + + return null; } /** @@ -123,9 +126,9 @@ protected function _put($token = null) * @param \Crud\Event\Subject $subject Event subject * @return \Cake\Datasource\EntityInterface|false */ - protected function _save(Subject $subject) + protected function _save(Subject $subject): EntityInterface|false { - $entity = $this->_table()->patchEntity( + $entity = $this->_model()->patchEntity( $subject->entity, $this->_request()->getData(), $this->saveOptions() @@ -135,7 +138,7 @@ protected function _save(Subject $subject) $this->_trigger('beforeSave', $subject); /** @var callable $callable */ - $callable = [$this->_table(), $this->saveMethod()]; + $callable = [$this->_model(), $this->saveMethod()]; /** @var \Cake\Datasource\EntityInterface|false $success */ $success = $callable($entity, $this->saveOptions()); diff --git a/src/Action/VerifyAction.php b/src/Action/VerifyAction.php index da1d7e9..93c2363 100644 --- a/src/Action/VerifyAction.php +++ b/src/Action/VerifyAction.php @@ -25,7 +25,7 @@ class VerifyAction extends BaseAction use ViewTrait; use ViewVarTrait; - protected $_defaultConfig = [ + protected array $_defaultConfig = [ 'enabled' => true, 'scope' => 'entity', 'findMethod' => 'all', @@ -70,10 +70,10 @@ class VerifyAction extends BaseAction /** * HTTP GET handler * - * @param string $token Token - * @return \Cake\Http\Response|null|void + * @param string|null $token Token + * @return \Cake\Http\Response|null */ - protected function _get($token = null) + protected function _get(?string $token = null): ?Response { $token = $this->_token($token); $entity = $this->_verify($token); @@ -83,6 +83,8 @@ protected function _get($token = null) } $this->_error(); + + return null; } /** @@ -91,9 +93,9 @@ protected function _get($token = null) * @param \Cake\Datasource\EntityInterface $entity Entity * @return \Cake\Datasource\EntityInterface|false */ - protected function _save(EntityInterface $entity) + protected function _save(EntityInterface $entity): EntityInterface|false { - $entity = $this->_table()->patchEntity( + $entity = $this->_model()->patchEntity( $entity, ['verified' => true], $this->saveOptions() @@ -103,7 +105,7 @@ protected function _save(EntityInterface $entity) $this->_trigger('beforeSave', $subject); /** @var callable $callback */ - $callback = [$this->_table(), $this->saveMethod()]; + $callback = [$this->_model(), $this->saveMethod()]; /** @var \Cake\Datasource\EntityInterface|false $success */ $success = $callback($entity, $this->saveOptions()); diff --git a/src/Traits/VerifyTrait.php b/src/Traits/VerifyTrait.php index c91e48c..bae659e 100644 --- a/src/Traits/VerifyTrait.php +++ b/src/Traits/VerifyTrait.php @@ -5,6 +5,9 @@ use Cake\Datasource\EntityInterface; +/** + * @method \Cake\ORM\Table _model() + */ trait VerifyTrait { /** @@ -51,13 +54,11 @@ protected function _verify(?string $token): EntityInterface $subject = $this->_subject(); $subject->set([ 'token' => $token, - 'repository' => $this->_table(), - 'query' => $this->_table()->find( + 'repository' => $this->_model(), + 'query' => $this->_model()->find( $this->findMethod(), - [ - 'token' => $token, - 'conditions' => [$this->_table()->aliasField($this->getConfig('tokenField')) => $token], - ] + token: $token, + conditions: [$this->_model()->aliasField($this->getConfig('tokenField')) => $token], ), ]); @@ -90,7 +91,7 @@ protected function _verify(?string $token): EntityInterface * @return void * @throws \Exception */ - protected function _tokenError($error = 'tokenNotFound'): void + protected function _tokenError(string $error = 'tokenNotFound'): void { $subject = $this->_subject(['success' => false]); $this->_trigger($error, $subject);