diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a464da3bb..b3139a7d2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,7 +31,7 @@ jobs: name: PHP ${{ matrix.php }}-${{ matrix.os }} env: - extensions: fileinfo, pdo, pdo_sqlite, intl + extensions: fileinfo, pdo, pdo_sqlite, intl-70.1 key: cache-v1 YII_C3: true diff --git a/.github/workflows/rector.yml b/.github/workflows/rector.yml new file mode 100644 index 000000000..adacd735a --- /dev/null +++ b/.github/workflows/rector.yml @@ -0,0 +1,21 @@ +on: + pull_request: + paths-ignore: + - 'docs/**' + - 'README.md' + - 'CHANGELOG.md' + - '.gitignore' + - '.gitattributes' + - 'infection.json.dist' + - 'psalm.xml' + +name: rector + +jobs: + rector: + uses: yiisoft/actions/.github/workflows/rector.yml@master + with: + os: >- + ['ubuntu-latest'] + php: >- + ['8.0'] diff --git a/composer.json b/composer.json index 0997eeaa0..8d1879735 100644 --- a/composer.json +++ b/composer.json @@ -96,12 +96,14 @@ "codeception/module-cli": "^2.0", "codeception/module-phpbrowser": "^3.0", "phpunit/phpunit": "^9.5", + "rector/rector": "^0.14.3", "roave/infection-static-analysis-plugin": "^1.16", "roave/security-advisories": "dev-master", "spatie/phpunit-watcher": "^1.23", "vimeo/psalm": "^4.18", "yiisoft/translator-extractor": "^1.0", "yiisoft/yii-debug-viewer": "^3.0@dev", + "yiisoft/yii-gii": "^3.0@dev", "yiisoft/yii-testing": "dev-master" }, "autoload": { diff --git a/config/params-web.php b/config/params-web.php new file mode 100644 index 000000000..546cc0b98 --- /dev/null +++ b/config/params-web.php @@ -0,0 +1,20 @@ + [ + 'parameters' => [ + 'assetManager' => Reference::to(AssetManager::class), + 'urlGenerator' => Reference::to(UrlGeneratorInterface::class), + 'currentRoute' => Reference::to(CurrentRoute::class), + 'translator' => Reference::to(TranslatorInterface::class), + ], + ], +]; diff --git a/config/params.php b/config/params.php index 1700e32dd..aee78eb70 100644 --- a/config/params.php +++ b/config/params.php @@ -6,20 +6,21 @@ use App\ViewInjection\LayoutViewInjection; use App\ViewInjection\LinkTagsViewInjection; use App\ViewInjection\MetaTagsViewInjection; -use Yiisoft\Assets\AssetManager; +use Cycle\Database\Config\SQLite\FileConnectionConfig; +use Cycle\Database\Config\SQLiteDriverConfig; use Yiisoft\Cookies\CookieMiddleware; use Yiisoft\Definitions\Reference; use Yiisoft\ErrorHandler\Middleware\ErrorCatcher; -use Yiisoft\Router\CurrentRoute; +use Yiisoft\Form\Field\SubmitButton; use Yiisoft\Router\Middleware\Router; -use Yiisoft\Router\UrlGeneratorInterface; use Yiisoft\Session\SessionMiddleware; -use Yiisoft\Translator\TranslatorInterface; use Yiisoft\User\Login\Cookie\CookieLoginMiddleware; use Yiisoft\Yii\Console\Application; use Yiisoft\Yii\Console\Command\Serve; use Yiisoft\Yii\Cycle\Schema\Conveyor\AttributedSchemaConveyor; use Yiisoft\Yii\Middleware\Locale; +use Yiisoft\Yii\Cycle\Schema\Provider\FromConveyorSchemaProvider; +use Yiisoft\Yii\Cycle\Schema\Provider\PhpFileSchemaProvider; use Yiisoft\Yii\Sentry\SentryMiddleware; use Yiisoft\Yii\View\CsrfViewInjection; @@ -74,7 +75,7 @@ 'errorClass' => 'fw-bold fst-italic', 'hintClass' => 'form-text', 'fieldConfigs' => [ - \Yiisoft\Form\Field\SubmitButton::class => [ + SubmitButton::class => [ 'buttonClass()' => ['btn btn-primary btn-lg mt-3'], 'containerClass()' => ['d-grid gap-2 form-floating'], ], @@ -156,8 +157,8 @@ 'default' => ['connection' => 'sqlite'], ], 'connections' => [ - 'sqlite' => new \Cycle\Database\Config\SQLiteDriverConfig( - connection: new \Cycle\Database\Config\SQLite\FileConnectionConfig( + 'sqlite' => new SQLiteDriverConfig( + connection: new FileConnectionConfig( database: 'runtime/database.db' ) ), @@ -195,12 +196,12 @@ // \Yiisoft\Yii\Cycle\Schema\Provider\SimpleCacheSchemaProvider::class => ['key' => 'cycle-orm-cache-key'], // Store generated Schema in the file - \Yiisoft\Yii\Cycle\Schema\Provider\PhpFileSchemaProvider::class => [ - 'mode' => \Yiisoft\Yii\Cycle\Schema\Provider\PhpFileSchemaProvider::MODE_WRITE_ONLY, + PhpFileSchemaProvider::class => [ + 'mode' => PhpFileSchemaProvider::MODE_WRITE_ONLY, 'file' => 'runtime/schema.php', ], - \Yiisoft\Yii\Cycle\Schema\Provider\FromConveyorSchemaProvider::class => [ + FromConveyorSchemaProvider::class => [ 'generators' => [ Cycle\Schema\Generator\SyncTables::class, // sync table changes to database ], diff --git a/rector.php b/rector.php new file mode 100644 index 000000000..63713ce90 --- /dev/null +++ b/rector.php @@ -0,0 +1,22 @@ +paths([ + __DIR__ . '/src', + __DIR__ . '/tests', + ]); + + // register a single rule + $rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class); + + // define sets of rules + $rectorConfig->sets([ + LevelSetList::UP_TO_PHP_80, + ]); +}; diff --git a/src/Auth/IdentityRepository.php b/src/Auth/IdentityRepository.php index 559280c3a..817689609 100644 --- a/src/Auth/IdentityRepository.php +++ b/src/Auth/IdentityRepository.php @@ -16,11 +16,6 @@ public function __construct(private EntityWriter $entityWriter, Select $select) parent::__construct($select); } - /** - * @param string $id - * - * @return Identity|null - */ public function findIdentity(string $id): ?Identity { return $this->findOne(['user_id' => $id]); diff --git a/src/Blog/Archive/ArchiveRepository.php b/src/Blog/Archive/ArchiveRepository.php index 5988b83e3..e13bbe03b 100644 --- a/src/Blog/Archive/ArchiveRepository.php +++ b/src/Blog/Archive/ArchiveRepository.php @@ -92,8 +92,6 @@ public function getFullArchive(): DataReaderInterface /** * @param string $attr Can be 'day', 'month' or 'year' - * - * @return FragmentInterface */ private function extractFromDateColumn(string $attr): FragmentInterface { @@ -121,12 +119,8 @@ private function getDriver(): DriverInterface /** * @psalm-suppress UndefinedDocblockClass - * - * @param Select|SelectQuery $query - * - * @return EntityReader */ - private function prepareDataReader($query): EntityReader + private function prepareDataReader(Select|SelectQuery $query): EntityReader { return (new EntityReader($query))->withSort(Sort::only(['published_at'])->withOrder(['published_at' => 'desc'])); } diff --git a/src/Blog/Comment/Scope/PublicScope.php b/src/Blog/Comment/Scope/PublicScope.php index 1b3d3d30c..5ac8a2d80 100644 --- a/src/Blog/Comment/Scope/PublicScope.php +++ b/src/Blog/Comment/Scope/PublicScope.php @@ -14,11 +14,8 @@ */ final class PublicScope implements ConstrainInterface { - private ?array $publicOrCondition; - - public function __construct(?array $publicOrCondition = null) + public function __construct(private ?array $publicOrCondition = null) { - $this->publicOrCondition = $publicOrCondition; } public function apply(QueryBuilder $query): void diff --git a/src/Blog/Entity/Comment.php b/src/Blog/Entity/Comment.php index d49badd97..4b35ad42a 100644 --- a/src/Blog/Entity/Comment.php +++ b/src/Blog/Entity/Comment.php @@ -28,9 +28,6 @@ class Comment #[Column(type: 'bool', default: 'false', typecast: 'bool')] private bool $public = false; - #[Column(type: 'text')] - private string $content; - #[Column(type: 'datetime')] private DateTimeImmutable $created_at; @@ -51,9 +48,8 @@ class Comment private ?Post $post = null; private ?int $post_id = null; - public function __construct(string $content) + public function __construct(#[Column(type: 'text')] private string $content) { - $this->content = $content; $this->created_at = new DateTimeImmutable(); $this->updated_at = new DateTimeImmutable(); } diff --git a/src/Blog/Entity/Post.php b/src/Blog/Entity/Post.php index ec400787c..e5c9f4638 100644 --- a/src/Blog/Entity/Post.php +++ b/src/Blog/Entity/Post.php @@ -33,15 +33,9 @@ class Post #[Column(type: 'string(128)')] private string $slug; - #[Column(type: 'string(191)', default: '')] - private string $title = ''; - #[Column(type: 'bool', default: 'false', typecast: 'bool')] private bool $public = false; - #[Column(type: 'text')] - private string $content = ''; - #[Column(type: 'datetime')] private DateTimeImmutable $created_at; @@ -70,10 +64,8 @@ class Post #[HasMany(target: Comment::class)] private ArrayCollection $comments; - public function __construct(string $title = '', string $content = '') + public function __construct(#[Column(type: 'string(191)', default: '')] private string $title = '', #[Column(type: 'text')] private string $content = '') { - $this->title = $title; - $this->content = $content; $this->created_at = new DateTimeImmutable(); $this->updated_at = new DateTimeImmutable(); $this->tags = new PivotedCollection(); diff --git a/src/Blog/Entity/Tag.php b/src/Blog/Entity/Tag.php index ee8d1782d..b9e9aa183 100644 --- a/src/Blog/Entity/Tag.php +++ b/src/Blog/Entity/Tag.php @@ -20,9 +20,6 @@ class Tag #[Column(type: 'primary')] private ?int $id = null; - #[Column(type: 'string(191)')] - private string $label; - #[Column(type: 'datetime')] private DateTimeImmutable $created_at; @@ -32,9 +29,8 @@ class Tag #[ManyToMany(target: Post::class, though: PostTag::class, fkAction: 'CASCADE', indexCreate: false)] private PivotedCollection $posts; - public function __construct(string $label) + public function __construct(#[Column(type: 'string(191)')] private string $label) { - $this->label = $label; $this->created_at = new DateTimeImmutable(); $this->posts = new PivotedCollection(); } diff --git a/src/Blog/Tag/TagRepository.php b/src/Blog/Tag/TagRepository.php index 6b149a2c7..6372d829d 100644 --- a/src/Blog/Tag/TagRepository.php +++ b/src/Blog/Tag/TagRepository.php @@ -37,8 +37,6 @@ public function findByLabel(string $label): ?Tag } /** - * @param int $limit - * * @return DataReaderInterface Collection of Array('label' => 'Tag Label', 'count' => '8') */ public function getTagMentions(int $limit = 0): DataReaderInterface diff --git a/src/Command/Fixture/AddCommand.php b/src/Command/Fixture/AddCommand.php index a20dcf2dd..48f43a68a 100644 --- a/src/Command/Fixture/AddCommand.php +++ b/src/Command/Fixture/AddCommand.php @@ -122,15 +122,15 @@ private function addPosts(int $count): void for ($i = 0; $i < $count; ++$i) { /** @var User $postUser */ $postUser = $this->users[array_rand($this->users)]; - $post = new Post($this->faker->text(64), $this->faker->realText(rand(1000, 4000))); + $post = new Post($this->faker->text(64), $this->faker->realText(random_int(1000, 4000))); $postUser->addPost($post); - $public = rand(0, 2) > 0; + $public = random_int(0, 2) > 0; $post->setPublic($public); if ($public) { - $post->setPublishedAt(new DateTimeImmutable(date('r', rand(time(), strtotime('-2 years'))))); + $post->setPublishedAt(new DateTimeImmutable(date('r', random_int(time(), strtotime('-2 years'))))); } // link tags - $postTags = (array)array_rand($this->tags, rand(1, count($this->tags))); + $postTags = (array)array_rand($this->tags, random_int(1, count($this->tags))); foreach ($postTags as $tagId) { $tag = $this->tags[$tagId]; $post->addTag($tag); @@ -138,13 +138,13 @@ private function addPosts(int $count): void // $tag->addPost($post); } // add comments - $commentsCount = rand(0, $count); + $commentsCount = random_int(0, $count); for ($j = 0; $j <= $commentsCount; ++$j) { - $comment = new Comment($this->faker->realText(rand(100, 500))); - $commentPublic = rand(0, 3) > 0; + $comment = new Comment($this->faker->realText(random_int(100, 500))); + $commentPublic = random_int(0, 3) > 0; $comment->setPublic($commentPublic); if ($commentPublic) { - $comment->setPublishedAt(new DateTimeImmutable(date('r', rand(time(), strtotime('-1 years'))))); + $comment->setPublishedAt(new DateTimeImmutable(date('r', random_int(time(), strtotime('-1 years'))))); } $commentUser = $this->users[array_rand($this->users)]; $commentUser->addComment($comment); diff --git a/src/Command/Router/ListCommand.php b/src/Command/Router/ListCommand.php index 27c163499..8c6aa0a05 100644 --- a/src/Command/Router/ListCommand.php +++ b/src/Command/Router/ListCommand.php @@ -35,9 +35,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $routes = $this->routeCollection->getRoutes(); uasort( $routes, - static function (Route $a, Route $b) { - return ($a->getData('host') <=> $b->getData('host')) ?: ($a->getData('name') <=> $b->getData('name')); - } + static fn (Route $a, Route $b) => ($a->getData('host') <=> $b->getData('host')) ?: ($a->getData('name') <=> $b->getData('name')) ); $table->setHeaders(['Host', 'Methods', 'Name', 'Pattern', 'Defaults']); foreach ($routes as $route) { diff --git a/src/Controller/Actions/ApiInfo.php b/src/Controller/Actions/ApiInfo.php index 1f196079d..002d35473 100644 --- a/src/Controller/Actions/ApiInfo.php +++ b/src/Controller/Actions/ApiInfo.php @@ -16,11 +16,8 @@ */ final class ApiInfo implements MiddlewareInterface { - private DataResponseFactoryInterface $responseFactory; - - public function __construct(DataResponseFactoryInterface $responseFactory) + public function __construct(private DataResponseFactoryInterface $responseFactory) { - $this->responseFactory = $responseFactory; } /** diff --git a/src/Controller/SiteController.php b/src/Controller/SiteController.php index 88e2c5885..086730be2 100644 --- a/src/Controller/SiteController.php +++ b/src/Controller/SiteController.php @@ -9,9 +9,7 @@ final class SiteController { - private ViewRenderer $viewRenderer; - - public function __construct(ViewRenderer $viewRenderer) + public function __construct(private ViewRenderer $viewRenderer) { $this->viewRenderer = $viewRenderer->withController($this); } diff --git a/src/User/User.php b/src/User/User.php index e18bff9e4..4883f4961 100644 --- a/src/User/User.php +++ b/src/User/User.php @@ -26,9 +26,6 @@ class User #[Column(type: 'primary')] private ?int $id = null; - #[Column(type: 'string(48)')] - private string $login; - #[Column(type: 'string')] private string $passwordHash; @@ -53,9 +50,8 @@ class User #[HasMany(target: \App\Blog\Entity\Comment::class)] private ArrayCollection $comments; - public function __construct(string $login, string $password) + public function __construct(#[Column(type: 'string(48)')] private string $login, string $password) { - $this->login = $login; $this->created_at = new DateTimeImmutable(); $this->updated_at = new DateTimeImmutable(); $this->setPassword($password); diff --git a/src/User/UserRepository.php b/src/User/UserRepository.php index 9237fb044..c04689373 100644 --- a/src/User/UserRepository.php +++ b/src/User/UserRepository.php @@ -39,11 +39,6 @@ public function findAll(array $scope = [], array $orderBy = []): DataReaderInter ->orderBy($orderBy)); } - /** - * @param string $id - * - * @return User|null - */ public function findById(string $id): ?User { return $this->findByPK($id); diff --git a/tests/Cli/ConsoleCest.php b/tests/Cli/ConsoleCest.php index da71de4e2..995d0518f 100644 --- a/tests/Cli/ConsoleCest.php +++ b/tests/Cli/ConsoleCest.php @@ -34,8 +34,6 @@ public function testCommandListCommand(CliTester $I): void * Clear all data created with testCommandFixtureAdd(). * Clearing database prevents from getting errors during multiple continuous testing with other test, * what are based on empty database (eg, BlogPageCest) - * - * @param \App\Tests\CliTester $I */ public function testCommandCycleSchemaClear(CliTester $I): void {