diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 0000000..11bd52f --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,2 @@ +reviews: + request_changes_workflow: true diff --git a/demos/blog-publishing/composer.json b/demos/blog-publishing/composer.json index 8d1f927..a9c6b1a 100644 --- a/demos/blog-publishing/composer.json +++ b/demos/blog-publishing/composer.json @@ -18,13 +18,16 @@ "ray/di": "^2.18" }, "require-dev": { + "be-framework/psalm-plugin": "1.x-dev", "phpunit/phpunit": "^12" }, "scripts": { + "psalm": "psalm --show-info=false", "stree": "vendor/bin/stree $(ls -t var/log/*.json | head -1)", "stree:full": "vendor/bin/stree --full $(ls -t var/log/*.json | head -1)" }, "scripts-descriptions": { + "psalm": "Run static analysis with Psalm + Be Framework plugin.", "stree": "Render the latest var/log/*.json as a compact tree.", "stree:full": "Render the latest var/log/*.json as a semantic full tree with full props and close.profile details." } diff --git a/demos/blog-publishing/psalm.xml b/demos/blog-publishing/psalm.xml new file mode 100644 index 0000000..76620e9 --- /dev/null +++ b/demos/blog-publishing/psalm.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/blog-publishing/src/Exception/InvalidAuthorNameException.php b/demos/blog-publishing/src/Exception/InvalidAuthorNameException.php new file mode 100644 index 0000000..09e4601 --- /dev/null +++ b/demos/blog-publishing/src/Exception/InvalidAuthorNameException.php @@ -0,0 +1,16 @@ + 'Author name cannot be empty.', + 'ja' => '著者名は空にできません。' +])] +final class InvalidAuthorNameException extends DomainException +{ +} diff --git a/demos/blog-publishing/src/Exception/InvalidExcerptException.php b/demos/blog-publishing/src/Exception/InvalidExcerptException.php new file mode 100644 index 0000000..c1d444d --- /dev/null +++ b/demos/blog-publishing/src/Exception/InvalidExcerptException.php @@ -0,0 +1,16 @@ + 'Excerpt is too long. Must be 250 characters or fewer.', + 'ja' => '抜粋が長すぎます。250文字以下で入力してください。' +])] +final class InvalidExcerptException extends DomainException +{ +} diff --git a/demos/blog-publishing/src/Exception/InvalidHtmlBodyException.php b/demos/blog-publishing/src/Exception/InvalidHtmlBodyException.php new file mode 100644 index 0000000..ff31179 --- /dev/null +++ b/demos/blog-publishing/src/Exception/InvalidHtmlBodyException.php @@ -0,0 +1,16 @@ + 'HTML body cannot be empty.', + 'ja' => 'HTML本文は空にできません。' +])] +final class InvalidHtmlBodyException extends DomainException +{ +} diff --git a/demos/blog-publishing/src/Exception/InvalidSlugException.php b/demos/blog-publishing/src/Exception/InvalidSlugException.php new file mode 100644 index 0000000..db34fa0 --- /dev/null +++ b/demos/blog-publishing/src/Exception/InvalidSlugException.php @@ -0,0 +1,16 @@ + 'Invalid slug format. Use lowercase letters, digits, and hyphens only.', + 'ja' => 'スラッグの形式が不正です。小文字・数字・ハイフンのみ使用できます。' +])] +final class InvalidSlugException extends DomainException +{ +} diff --git a/demos/blog-publishing/src/Semantic/AuthorName.php b/demos/blog-publishing/src/Semantic/AuthorName.php index f03964c..5d8a52b 100644 --- a/demos/blog-publishing/src/Semantic/AuthorName.php +++ b/demos/blog-publishing/src/Semantic/AuthorName.php @@ -5,7 +5,7 @@ namespace Be\Pattern\BlogPublishing\Semantic; use Be\Framework\Attribute\Validate; -use InvalidArgumentException; +use Be\Pattern\BlogPublishing\Exception\InvalidAuthorNameException; /** * AuthorName - Semantic validation @@ -20,7 +20,7 @@ final class AuthorName public function validate(string $authorName): void { if (empty(trim($authorName))) { - throw new InvalidArgumentException('Author name cannot be empty'); + throw new InvalidAuthorNameException(); } } } diff --git a/demos/blog-publishing/src/Semantic/Excerpt.php b/demos/blog-publishing/src/Semantic/Excerpt.php index a28992e..125b05e 100644 --- a/demos/blog-publishing/src/Semantic/Excerpt.php +++ b/demos/blog-publishing/src/Semantic/Excerpt.php @@ -5,7 +5,7 @@ namespace Be\Pattern\BlogPublishing\Semantic; use Be\Framework\Attribute\Validate; -use InvalidArgumentException; +use Be\Pattern\BlogPublishing\Exception\InvalidExcerptException; /** * Excerpt - Semantic validation @@ -18,7 +18,7 @@ final class Excerpt public function validate(string $excerpt): void { if (mb_strlen($excerpt) > 250) { - throw new InvalidArgumentException('Excerpt too long'); + throw new InvalidExcerptException(); } } } diff --git a/demos/blog-publishing/src/Semantic/HtmlBody.php b/demos/blog-publishing/src/Semantic/HtmlBody.php index 0e785b4..bc61dce 100644 --- a/demos/blog-publishing/src/Semantic/HtmlBody.php +++ b/demos/blog-publishing/src/Semantic/HtmlBody.php @@ -5,7 +5,7 @@ namespace Be\Pattern\BlogPublishing\Semantic; use Be\Framework\Attribute\Validate; -use InvalidArgumentException; +use Be\Pattern\BlogPublishing\Exception\InvalidHtmlBodyException; /** * HtmlBody - Semantic validation @@ -18,7 +18,7 @@ final class HtmlBody public function validate(string $htmlBody): void { if (empty(trim(strip_tags($htmlBody)))) { - throw new InvalidArgumentException('HTML body cannot be empty'); + throw new InvalidHtmlBodyException(); } } } diff --git a/demos/blog-publishing/src/Semantic/Slug.php b/demos/blog-publishing/src/Semantic/Slug.php index e03dd1f..e36754c 100644 --- a/demos/blog-publishing/src/Semantic/Slug.php +++ b/demos/blog-publishing/src/Semantic/Slug.php @@ -5,7 +5,7 @@ namespace Be\Pattern\BlogPublishing\Semantic; use Be\Framework\Attribute\Validate; -use InvalidArgumentException; +use Be\Pattern\BlogPublishing\Exception\InvalidSlugException; /** * Slug - Semantic validation @@ -18,7 +18,7 @@ final class Slug public function validate(string $slug): void { if (!preg_match('/^[a-z0-9-]+$/', $slug)) { - throw new InvalidArgumentException('Invalid slug format'); + throw new InvalidSlugException(); } } } diff --git a/demos/contact-form/composer.json b/demos/contact-form/composer.json index ece0256..d4f56ff 100644 --- a/demos/contact-form/composer.json +++ b/demos/contact-form/composer.json @@ -18,13 +18,16 @@ "ray/di": "^2.18" }, "require-dev": { + "be-framework/psalm-plugin": "1.x-dev", "phpunit/phpunit": "^12" }, "scripts": { + "psalm": "psalm --show-info=false", "stree": "vendor/bin/stree $(ls -t var/log/*.json | head -1)", "stree:full": "vendor/bin/stree --full $(ls -t var/log/*.json | head -1)" }, "scripts-descriptions": { + "psalm": "Run static analysis with Psalm + Be Framework plugin.", "stree": "Render the latest var/log/*.json as a compact tree.", "stree:full": "Render the latest var/log/*.json as a semantic full tree with full props and close.profile details." } diff --git a/demos/contact-form/psalm.xml b/demos/contact-form/psalm.xml new file mode 100644 index 0000000..76620e9 --- /dev/null +++ b/demos/contact-form/psalm.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/hello-world/composer.json b/demos/hello-world/composer.json index 795c46a..f0b1870 100644 --- a/demos/hello-world/composer.json +++ b/demos/hello-world/composer.json @@ -18,17 +18,20 @@ "ray/di": "^2.18" }, "require-dev": { + "be-framework/psalm-plugin": "1.x-dev", "phpunit/phpunit": "^12" }, "scripts": { "dev": "php bin/be.php 'hello?name=World'", "profile": "XDEBUG_MODE=trace php -d xdebug.use_compression=0 -d extension=xhprof.so bin/be.php 'hello?name=World'", + "psalm": "psalm --show-info=false", "stree": "vendor/bin/stree $(ls -t var/log/*.json | head -1)", "stree:full": "vendor/bin/stree --full $(ls -t var/log/*.json | head -1)" }, "scripts-descriptions": { "dev": "Run the Hello World demo in dev mode; writes a semantic log to var/log/*.json without Xdebug/XHProf profiling.", "profile": "Run the Hello World demo with semantic profiling; writes a semantic log to var/log/*.json with uncompressed Xdebug trace and xhprof enabled.", + "psalm": "Run static analysis with Psalm + Be Framework plugin.", "stree": "Render the latest var/log/*.json as a compact tree.", "stree:full": "Render the latest var/log/*.json as a semantic full tree with full props and close.profile details." } diff --git a/demos/hello-world/psalm.xml b/demos/hello-world/psalm.xml new file mode 100644 index 0000000..76620e9 --- /dev/null +++ b/demos/hello-world/psalm.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/insurance-claim/composer.json b/demos/insurance-claim/composer.json index a629a17..a75b2a4 100644 --- a/demos/insurance-claim/composer.json +++ b/demos/insurance-claim/composer.json @@ -17,13 +17,16 @@ "ray/di": "^2.18" }, "require-dev": { + "be-framework/psalm-plugin": "1.x-dev", "phpunit/phpunit": "^12" }, "scripts": { + "psalm": "psalm --show-info=false", "stree": "vendor/bin/stree $(ls -t var/log/*.json | head -1)", "stree:full": "vendor/bin/stree --full $(ls -t var/log/*.json | head -1)" }, "scripts-descriptions": { + "psalm": "Run static analysis with Psalm + Be Framework plugin.", "stree": "Render the latest var/log/*.json as a compact tree.", "stree:full": "Render the latest var/log/*.json as a semantic full tree with full props and close.profile details." } diff --git a/demos/insurance-claim/psalm.xml b/demos/insurance-claim/psalm.xml new file mode 100644 index 0000000..76620e9 --- /dev/null +++ b/demos/insurance-claim/psalm.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/loan-application/composer.json b/demos/loan-application/composer.json index 920056a..b1ecc44 100644 --- a/demos/loan-application/composer.json +++ b/demos/loan-application/composer.json @@ -17,13 +17,16 @@ "ray/di": "^2.18" }, "require-dev": { + "be-framework/psalm-plugin": "1.x-dev", "phpunit/phpunit": "^12" }, "scripts": { + "psalm": "psalm --show-info=false", "stree": "vendor/bin/stree $(ls -t var/log/*.json | head -1)", "stree:full": "vendor/bin/stree --full $(ls -t var/log/*.json | head -1)" }, "scripts-descriptions": { + "psalm": "Run static analysis with Psalm + Be Framework plugin.", "stree": "Render the latest var/log/*.json as a compact tree.", "stree:full": "Render the latest var/log/*.json as a semantic full tree with full props and close.profile details." } diff --git a/demos/loan-application/psalm.xml b/demos/loan-application/psalm.xml new file mode 100644 index 0000000..76620e9 --- /dev/null +++ b/demos/loan-application/psalm.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/medical-triage/composer.json b/demos/medical-triage/composer.json index 76028f6..1a5f370 100644 --- a/demos/medical-triage/composer.json +++ b/demos/medical-triage/composer.json @@ -17,17 +17,20 @@ "ray/di": "^2.18" }, "require-dev": { + "be-framework/psalm-plugin": "1.x-dev", "phpunit/phpunit": "^12" }, "scripts": { "dev": "php bin/be.php", "profile": "XDEBUG_MODE=trace php -d xdebug.use_compression=0 -d extension=xhprof.so bin/be.php", + "psalm": "psalm --show-info=false", "stree": "vendor/bin/stree $(ls -t var/log/*.json | head -1)", "stree:full": "vendor/bin/stree --full $(ls -t var/log/*.json | head -1)" }, "scripts-descriptions": { "dev": "Run the Medical Triage demo in dev mode; writes a semantic log to var/log/*.json without Xdebug/XHProf profiling.", "profile": "Run the Medical Triage demo with semantic profiling; writes a semantic log to var/log/*.json with uncompressed Xdebug trace and xhprof enabled.", + "psalm": "Run static analysis with Psalm + Be Framework plugin.", "stree": "Render the latest var/log/*.json as a compact tree.", "stree:full": "Render the latest var/log/*.json as a semantic full tree with full props and close.profile details." } diff --git a/demos/medical-triage/psalm.xml b/demos/medical-triage/psalm.xml new file mode 100644 index 0000000..76620e9 --- /dev/null +++ b/demos/medical-triage/psalm.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/order-processing/composer.json b/demos/order-processing/composer.json index 47fa0a9..6900217 100644 --- a/demos/order-processing/composer.json +++ b/demos/order-processing/composer.json @@ -17,17 +17,20 @@ "ray/di": "^2.18" }, "require-dev": { + "be-framework/psalm-plugin": "1.x-dev", "phpunit/phpunit": "^12" }, "scripts": { "dev": "php bin/be.php", "profile": "XDEBUG_MODE=trace php -d xdebug.use_compression=0 -d extension=xhprof.so bin/be.php", + "psalm": "psalm --show-info=false", "stree": "vendor/bin/stree $(ls -t var/log/*.json | head -1)", "stree:full": "vendor/bin/stree --full $(ls -t var/log/*.json | head -1)" }, "scripts-descriptions": { "dev": "Run the Order Processing demo in dev mode; writes a semantic log to var/log/*.json without Xdebug/XHProf profiling.", "profile": "Run the Order Processing demo with semantic profiling; writes a semantic log to var/log/*.json with uncompressed Xdebug trace and xhprof enabled.", + "psalm": "Run static analysis with Psalm + Be Framework plugin.", "stree": "Render the latest var/log/*.json as a compact tree.", "stree:full": "Render the latest var/log/*.json as a semantic full tree with full props and close.profile details." } diff --git a/demos/order-processing/psalm.xml b/demos/order-processing/psalm.xml new file mode 100644 index 0000000..76620e9 --- /dev/null +++ b/demos/order-processing/psalm.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/user-registration/composer.json b/demos/user-registration/composer.json index 0b77b6b..6c56de4 100644 --- a/demos/user-registration/composer.json +++ b/demos/user-registration/composer.json @@ -19,17 +19,20 @@ "ray/di": "^2.18" }, "require-dev": { + "be-framework/psalm-plugin": "1.x-dev", "phpunit/phpunit": "^12" }, "scripts": { "dev": "php bin/be.php", "profile": "XDEBUG_MODE=trace php -d xdebug.use_compression=0 -d extension=xhprof.so bin/be.php", + "psalm": "psalm --show-info=false", "stree": "vendor/bin/stree $(ls -t var/log/*.json | head -1)", "stree:full": "vendor/bin/stree --full $(ls -t var/log/*.json | head -1)" }, "scripts-descriptions": { "dev": "Run the User Registration demo in dev mode; writes a semantic log to var/log/*.json without Xdebug/XHProf profiling.", "profile": "Run the User Registration demo with semantic profiling; writes a semantic log to var/log/*.json with uncompressed Xdebug trace and xhprof enabled.", + "psalm": "Run static analysis with Psalm + Be Framework plugin.", "stree": "Render the latest var/log/*.json as a compact tree.", "stree:full": "Render the latest var/log/*.json as a semantic full tree with full props and close.profile details." } diff --git a/demos/user-registration/psalm.xml b/demos/user-registration/psalm.xml new file mode 100644 index 0000000..76620e9 --- /dev/null +++ b/demos/user-registration/psalm.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/user-registration/src/Exception/InvalidAvatarUrlException.php b/demos/user-registration/src/Exception/InvalidAvatarUrlException.php new file mode 100644 index 0000000..69ceda2 --- /dev/null +++ b/demos/user-registration/src/Exception/InvalidAvatarUrlException.php @@ -0,0 +1,16 @@ + 'Invalid avatar URL.', + 'ja' => 'アバターURLの形式が不正です。' +])] +final class InvalidAvatarUrlException extends DomainException +{ +} diff --git a/demos/user-registration/src/Exception/InvalidHashedPasswordException.php b/demos/user-registration/src/Exception/InvalidHashedPasswordException.php new file mode 100644 index 0000000..d861e10 --- /dev/null +++ b/demos/user-registration/src/Exception/InvalidHashedPasswordException.php @@ -0,0 +1,16 @@ + 'Invalid password hash format.', + 'ja' => 'パスワードハッシュの形式が不正です。' +])] +final class InvalidHashedPasswordException extends DomainException +{ +} diff --git a/demos/user-registration/src/Semantic/AvatarUrl.php b/demos/user-registration/src/Semantic/AvatarUrl.php index 7205b63..be48f92 100644 --- a/demos/user-registration/src/Semantic/AvatarUrl.php +++ b/demos/user-registration/src/Semantic/AvatarUrl.php @@ -5,7 +5,7 @@ namespace Be\Pattern\UserRegistration\Semantic; use Be\Framework\Attribute\Validate; -use InvalidArgumentException; +use Be\Pattern\UserRegistration\Exception\InvalidAvatarUrlException; /** * AvatarUrl - Semantic validation @@ -20,7 +20,7 @@ final class AvatarUrl public function validate(string $avatarUrl): void { if (filter_var($avatarUrl, FILTER_VALIDATE_URL) === false) { - throw new InvalidArgumentException('Invalid avatar URL'); + throw new InvalidAvatarUrlException(); } } } diff --git a/demos/user-registration/src/Semantic/HashedPassword.php b/demos/user-registration/src/Semantic/HashedPassword.php index 09d4526..b4174a0 100644 --- a/demos/user-registration/src/Semantic/HashedPassword.php +++ b/demos/user-registration/src/Semantic/HashedPassword.php @@ -5,7 +5,7 @@ namespace Be\Pattern\UserRegistration\Semantic; use Be\Framework\Attribute\Validate; -use InvalidArgumentException; +use Be\Pattern\UserRegistration\Exception\InvalidHashedPasswordException; /** * HashedPassword - Semantic validation @@ -19,7 +19,7 @@ public function validate(string $hashedPassword): void { // Bcrypt hashes start with $2y$ or $2a$ and are 60 characters if (!preg_match('/^\$2[aby]\$\d{2}\$.{53}$/', $hashedPassword)) { - throw new InvalidArgumentException('Invalid password hash format'); + throw new InvalidHashedPasswordException(); } } }