From 6f21ddf37483bad65cea5f4f85cc7e7c017421a2 Mon Sep 17 00:00:00 2001 From: ADmad Date: Sun, 22 Mar 2026 12:02:18 +0530 Subject: [PATCH 1/3] Fix static analysis errors --- phpstan.neon | 1 - psalm.xml | 3 ++ src/Model/Behavior/TrashBehavior.php | 31 ++++++++++++++----- .../Model/Behavior/TrashBehaviorTest.php | 2 +- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 1d3456a..1bd2f44 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -3,6 +3,5 @@ parameters: paths: - src/ ignoreErrors: - - '#Call to an undefined method Cake\\ORM\\Table::cascadingRestoreTrash\(\)#' - identifier: missingType.iterableValue - identifier: missingType.generics diff --git a/psalm.xml b/psalm.xml index b192091..7cf2785 100644 --- a/psalm.xml +++ b/psalm.xml @@ -23,5 +23,8 @@ + + + diff --git a/src/Model/Behavior/TrashBehavior.php b/src/Model/Behavior/TrashBehavior.php index 3afdc2e..fa1e685 100644 --- a/src/Model/Behavior/TrashBehavior.php +++ b/src/Model/Behavior/TrashBehavior.php @@ -160,7 +160,12 @@ public function trash(EntityInterface $entity, array $options = []): bool } } - $entity->patch([$this->getTrashField(false) => new DateTime()]); + /** @phpstan-ignore function.alreadyNarrowedType */ + if (method_exists($entity, 'patch')) { + $entity->patch([$this->getTrashField(false) => new DateTime()]); + } else { + $entity->set($this->getTrashField(false), new DateTime()); + } return (bool)$this->_table->save($entity, $options); } @@ -287,7 +292,13 @@ public function restoreTrash(?EntityInterface $entity = null, array $options = [ if ($entity->isDirty()) { throw new CakeException('Can not restore from a dirty entity.'); } - $entity->patch($data, ['guard' => false]); + + /** @phpstan-ignore function.alreadyNarrowedType */ + if (method_exists($entity, 'patch')) { + $entity->patch($data, ['guard' => false]); + } else { + $entity->set($data, ['guard' => false]); + } return $this->_table->save($entity, $options); } @@ -307,15 +318,18 @@ public function cascadingRestoreTrash( array $options = [] ): bool|int|EntityInterface { $result = $this->restoreTrash($entity, $options); + $return = $result; $associations = $this->_table->associations()->getByType(['HasOne', 'HasMany']); foreach ($associations as $association) { if ($this->_isRecursable($association, $this->_table)) { if ($entity === null) { - if ($result > 1) { + if ($result > 0) { /** @var \Muffin\Trash\Model\Behavior\TrashBehavior $behavior */ $behavior = $association->getTarget()->getBehavior('Trash'); - $result += $behavior->cascadingRestoreTrash(null, $options); + if ($behavior->cascadingRestoreTrash(null, $options) === false) { + $return = false; + } } } else { /** @var list $foreignKey */ @@ -324,20 +338,21 @@ public function cascadingRestoreTrash( $bindingKey = (array)$association->getBindingKey(); $conditions = array_combine($foreignKey, $entity->extract($bindingKey)); - foreach ($association->find('withTrashed')->where($conditions) as $related) { + /** @var \Cake\Datasource\EntityInterface $related */ + foreach ($association->find('withTrashed')->where($conditions)->all() as $related) { /** @var \Muffin\Trash\Model\Behavior\TrashBehavior $behavior */ $behavior = $association->getTarget()->getBehavior('Trash'); if ( - !$behavior->cascadingRestoreTrash($related, ['_primary' => false] + $options) + $behavior->cascadingRestoreTrash($related, ['_primary' => false] + $options) === false ) { - $result = false; + $return = false; } } } } } - return $result; + return $return; } /** diff --git a/tests/TestCase/Model/Behavior/TrashBehaviorTest.php b/tests/TestCase/Model/Behavior/TrashBehaviorTest.php index 7d3cc04..9aa16d5 100644 --- a/tests/TestCase/Model/Behavior/TrashBehaviorTest.php +++ b/tests/TestCase/Model/Behavior/TrashBehaviorTest.php @@ -766,7 +766,7 @@ public function testCascadingUntrashAll() $this->assertNotEmpty($article->composite_articles_users[0]->trashed); $this->assertInstanceOf(DateTime::class, $article->composite_articles_users[0]->trashed); - $this->assertEquals(8, $this->Articles->getBehavior('Trash')->cascadingRestoreTrash()); + $this->assertEquals(3, $this->Articles->getBehavior('Trash')->cascadingRestoreTrash()); $article = $this->Articles ->find() From 007887b3d6c5bf8a41e0a2f4e90e082eb4773b80 Mon Sep 17 00:00:00 2001 From: ADmad Date: Sun, 22 Mar 2026 12:20:44 +0530 Subject: [PATCH 2/3] Fix phpcs errors --- phpcs.xml | 3 +- src/Model/Behavior/TrashBehavior.php | 4 +-- .../Model/Behavior/TrashBehaviorTest.php | 32 +++++++++---------- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/phpcs.xml b/phpcs.xml index 3feda30..9e6cb3e 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -1,6 +1,7 @@ - + src/ + tests/ diff --git a/src/Model/Behavior/TrashBehavior.php b/src/Model/Behavior/TrashBehavior.php index fa1e685..848b42e 100644 --- a/src/Model/Behavior/TrashBehavior.php +++ b/src/Model/Behavior/TrashBehavior.php @@ -263,7 +263,7 @@ public function trashAll(mixed $conditions): int { return $this->_table->updateAll( [$this->getTrashField(false) => new DateTime()], - $conditions + $conditions, ); } @@ -315,7 +315,7 @@ public function restoreTrash(?EntityInterface $entity = null, array $options = [ */ public function cascadingRestoreTrash( ?EntityInterface $entity = null, - array $options = [] + array $options = [], ): bool|int|EntityInterface { $result = $this->restoreTrash($entity, $options); $return = $result; diff --git a/tests/TestCase/Model/Behavior/TrashBehaviorTest.php b/tests/TestCase/Model/Behavior/TrashBehaviorTest.php index 9aa16d5..b41f678 100644 --- a/tests/TestCase/Model/Behavior/TrashBehaviorTest.php +++ b/tests/TestCase/Model/Behavior/TrashBehaviorTest.php @@ -58,7 +58,7 @@ public function setUp(): void $this->CompositeArticlesUsers = $this->getTableLocator()->get( 'Muffin/Trash.CompositeArticlesUsers', - ['table' => 'trash_composite_articles_users'] + ['table' => 'trash_composite_articles_users'], ); $this->CompositeArticlesUsers->addBehavior('Muffin/Trash.Trash'); @@ -129,7 +129,7 @@ public function testBeforeFindWithTrashFieldInComparison() $query = $this->Articles->find('all'); $result = $query->where( - [$this->Articles->aliasField('trashed') . ' >= ' => new DateTime('-1 day')] + [$this->Articles->aliasField('trashed') . ' >= ' => new DateTime('-1 day')], )->toArray(); $this->assertCount(2, $result); } @@ -179,7 +179,7 @@ function (Event $event, EntityInterface $entity, ArrayObject $options) { $entity->setError('id', 'Save aborted'); $event->setResult(false); $event->stopPropagation(); - } + }, ); $result = $this->Articles->delete($article); @@ -223,7 +223,7 @@ function (Event $event, EntityInterface $entity, ArrayObject $options) use (&$ha if (isset($options['deleteOptions'])) { $hasDeleteOptionsBefore = true; } - } + }, ); $this->Comments->getEventManager()->on( 'Model.afterDelete', @@ -231,7 +231,7 @@ function (Event $event, EntityInterface $entity, ArrayObject $options) use (&$ha if (isset($options['deleteOptions'])) { $hasDeleteOptionsAfter = true; } - } + }, ); $article = $this->Articles->get(1); @@ -265,24 +265,24 @@ function (Event $event, EntityInterface $entity, ArrayObject $options) use (&$ma if (isset($options['deleteOptions'])) { $mainHasDeleteOptions = true; } - } + }, ); $this->Comments->getEventManager()->on( 'Model.beforeSave', function ( Event $event, EntityInterface $entity, - ArrayObject $options + ArrayObject $options, ) use ( &$dependentHasDeleteOptions, - &$dependentIsNotPrimary + &$dependentIsNotPrimary, ) { if (isset($options['deleteOptions'])) { $dependentHasDeleteOptions = true; } $dependentIsNotPrimary = $options['_primary'] === false; - } + }, ); $article = $this->Articles->get(1); @@ -329,7 +329,7 @@ public function testTrash() $this->getTableLocator() ->get('ArticlesUsers', ['table' => 'trash_articles_users']) ->find() - ->count() + ->count(), ); } @@ -595,24 +595,24 @@ function (Event $event, EntityInterface $entity, ArrayObject $options) use (&$ma if (isset($options['restoreOptions'])) { $mainHasRestoreOptions = true; } - } + }, ); $this->Comments->getEventManager()->on( 'Model.beforeSave', function ( Event $event, EntityInterface $entity, - ArrayObject $options + ArrayObject $options, ) use ( &$dependentHasRestoreOptions, - &$dependentIsNotPrimary + &$dependentIsNotPrimary, ) { if (isset($options['restoreOptions'])) { $dependentHasRestoreOptions = true; } $dependentIsNotPrimary = $options['_primary'] === false; - } + }, ); $result = $this->Articles->getBehavior('Trash')->cascadingRestoreTrash($article, [ @@ -690,7 +690,7 @@ public function testCascadingUntrashEntity() $this->assertInstanceOf( EntityInterface::class, - $this->Articles->getBehavior('Trash')->cascadingRestoreTrash($article) + $this->Articles->getBehavior('Trash')->cascadingRestoreTrash($article), ); $article = $this->Articles @@ -903,7 +903,7 @@ public function testGetTrashFieldSchemaIntrospection() { $this->assertEquals( 'Articles.trashed', - $this->Articles->behaviors()->get('Trash')->getTrashField() + $this->Articles->behaviors()->get('Trash')->getTrashField(), ); } From 208d092266cc88e61e42996c0335e508bc5bae04 Mon Sep 17 00:00:00 2001 From: ADmad Date: Sun, 22 Mar 2026 12:29:42 +0530 Subject: [PATCH 3/3] Update phpunit constraints --- composer.json | 2 +- phpunit.xml.dist | 1 - tests/TestCase/Model/Behavior/TrashBehaviorTest.php | 12 +++++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index f728d07..9631e10 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,7 @@ }, "require-dev": { "cakephp/cakephp": "^5.0.0", - "phpunit/phpunit": "^10.1.0", + "phpunit/phpunit": "^10.5.58 || ^11.5.3 || ^12.4", "cakephp/cakephp-codesniffer": "^5.0" }, "scripts": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 6c6d9f9..c5212ca 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -3,7 +3,6 @@ - diff --git a/tests/TestCase/Model/Behavior/TrashBehaviorTest.php b/tests/TestCase/Model/Behavior/TrashBehaviorTest.php index b41f678..7f7b2c1 100644 --- a/tests/TestCase/Model/Behavior/TrashBehaviorTest.php +++ b/tests/TestCase/Model/Behavior/TrashBehaviorTest.php @@ -16,6 +16,7 @@ use Cake\TestSuite\TestCase; use InvalidArgumentException; use Muffin\Trash\Model\Behavior\TrashBehavior; +use PHPUnit\Framework\Attributes\DataProvider; class TrashBehaviorTest extends TestCase { @@ -923,11 +924,11 @@ public function testImpEInvalidArgumentException() /** * Test the implementedEvents method. * - * @dataProvider provideConfigsForImplementedEventsTest * @param array $config Initial behavior config. * @param array $implementedEvents Expected implementedEvents. * @return void */ + #[DataProvider('provideConfigsForImplementedEventsTest')] public function testImplementedEvents(array $config, array $implementedEvents) { $trash = new TrashBehavior($this->Users, $config); @@ -942,6 +943,9 @@ public function testImplementedEvents(array $config, array $implementedEvents) */ public static function provideConfigsForImplementedEventsTest() { + $callable = function () { + }; + return [ 'No event config inherits default events' => [ [], @@ -1001,8 +1005,7 @@ public static function provideConfigsForImplementedEventsTest() [ 'events' => [ 'Model.beforeDelete' => [ - 'callable' => function () { - }, + 'callable' => $callable, ], 'Model.beforeFind' => [ 'callable' => ['', 'beforeDelete'], @@ -1012,8 +1015,7 @@ public static function provideConfigsForImplementedEventsTest() ], [ 'Model.beforeDelete' => [ - 'callable' => function () { - }, + 'callable' => $callable, ], 'Model.beforeFind' => [ 'callable' => ['', 'beforeDelete'],