Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
13 commits
Select commit Hold shift + click to select a range
4193579
refactor: アノテーションを PHP 8 Attributes に移行し依存関係を更新
apple-x-co Dec 27, 2025
d90abb9
docs: README のサンプルコードを PHP 8 Attributes 形式に更新 migrate-attribute
apple-x-co Dec 31, 2025
e192dc4
test: PHPUnit 設定を最新形式に更新しテストコードを PHP 8 対応に改善 migrate-attribute
apple-x-co Jan 6, 2026
d6e5bba
ci: Scrutinizer 設定から `php_mess_detector` を無効化 migrate-attribute
apple-x-co Jan 6, 2026
f4e605a
docs: README の `VndError` 表記を Attributes 形式に更新 migrate-attribute
apple-x-co Jan 6, 2026
7bcb17d
ci: Scrutinizer 設定を簡素化し PHP 8.0 ビルド環境に移行 migrate-attribute
apple-x-co Jan 7, 2026
67df167
test: `AuraInputInterceptorTest` の重複した `expectException` 呼び出しを削除 migr…
apple-x-co Jan 7, 2026
47c4b50
ci: Scrutinizer の PHP バージョンを 8.2 に更新し設定構造を最適化 migrate-attribute
apple-x-co Jan 7, 2026
280117b
test: 未使用の `invalidControllerProvider` メソッドをコメントアウト migrate-attribute
apple-x-co Jan 7, 2026
9f408e2
style: 全ソースファイルに `declare(strict_types=1)` を追加し厳密な型チェックを有効化 migrate-a…
apple-x-co Feb 7, 2026
677d1cb
style: namespace 後に空行追加、use 文を整理し FQCN を削減 migrate-attribute
apple-x-co Feb 14, 2026
46414e8
refactor: プロパティと返り値に型宣言を追加し PHP 8 型システムに対応 migrate-attribute
apple-x-co Feb 14, 2026
dfafacb
refactor: Doctrine Annotations を削除し PHP 8 Attributes に完全移行 migrate-at…
apple-x-co Feb 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ build
vendor/
composer.phar
composer.lock
tests/tmp/*
!tests/tmp/.placefolder
.phpunit.result.cache
27 changes: 10 additions & 17 deletions .scrutinizer.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
build:
nodes:
analysis:
tests:
override:
- php-scrutinizer-run
environment:
php:
version: 8.2

filter:
paths: ["src/*"]
tools:
external_code_coverage: true
php_code_coverage:
timeout: 1200
php_sim: true
php_mess_detector: true
php_pdepend: true
php_analyzer: true
php_cpd: true
php_mess_detector:
enabled: true
config:
ruleset: ./phpmd.xml
php_code_sniffer:
enabled: true
config:
ruleset: ./phpcs.xml
26 changes: 7 additions & 19 deletions README.JA.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,18 +95,14 @@ class MyController
*/
protected $contactForm;

/**
* @Inject
* @Named("contact_form")
*/
#[Inject]
#[Named("contact_form")]
public function setForm(FormInterface $form)
{
$this->contactForm = $form;
}

/**
* @FormValidation(form="contactForm", onFailure="badRequestAction")
*/
#[FormValidation(form: "contactForm", onFailure: "badRequestAction")]
public function createAction()
{
// validation success
Expand Down Expand Up @@ -151,9 +147,7 @@ use Ray\WebFormModule\Annotation\InputValidation;

class Foo
{
/**
* @InputValidation(form="form1")
*/
#[InputValidation(form: "form1")]
public function createAction($name)
{
// ...
Expand Down Expand Up @@ -190,17 +184,11 @@ echo $e->error;
//}
```

`@VndError`アノテーションで`vnd.error+json`に必要な情報を加えることができます。
`#[VndError]`属性で`vnd.error+json`に必要な情報を加えることができます。

```php
/**
* @FormValidation(form="contactForm")
* @VndError(
* message="foo validation failed",
* logref="a1000", path="/path/to/error",
* href={"_self"="/path/to/error", "help"="/path/to/help"}
* )
*/
#[FormValidation(form: "contactForm")]
#[VndError(message: "foo validation failed", logref: "a1000", path: "/path/to/error", href: ["_self" => "/path/to/error", "help" => "/path/to/help"])]
```

このオプションのモジュールはAPIアプリケーションの時に有用です。
Expand Down
21 changes: 6 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,21 +103,18 @@ class MyController
*/
protected $contactForm;

/**
* @Inject
* @Named("contact_form")
*/
#[Inject]
#[Named("contact_form")]
public function setForm(FormInterface $form)
{
$this->contactForm = $form;
}

/**
* @FormValidation(form="contactForm", onFailure="badRequestAction")
*/
#[FormValidation(form: "contactForm", onFailure: "badRequestAction")]
public function createAction()
{
// validation success
// More detail for `vnd.error+json` can be added with `#[VndError]`.
}

public function badRequestAction()
Expand Down Expand Up @@ -186,14 +183,8 @@ echo $e->error;
More detail for `vnd.error+json`can be add with `@VndError` annotation.

```php
/**
* @FormValidation(form="contactForm")
* @VndError(
* message="foo validation failed",
* logref="a1000", path="/path/to/error",
* href={"_self"="/path/to/error", "help"="/path/to/help"}
* )
*/
#[FormValidation(form: "contactForm")]
#[VndError(message: "foo validation failed", logref: "a1000", path: "/path/to/error", href: ["_self" => "/path/to/error", "help" => "/path/to/help"])]
```

This optional module is handy for API application.
Expand Down
12 changes: 9 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@
"Ray.Di module"
],
"require": {
"php": ">=7.0.0",
"ray/di": "^2.7",
"php": ">=8.0.0",
"ray/di": "^2.16",
"ray/aop": "^2.14",
"aura/input": "^1.2",
"aura/filter": "^2.3|3.x-dev",
"aura/html": "^2.5",
"ray/aura-session-module": "^1.1"
},
"require-dev": {
"phpunit/phpunit": "^5.7.13"
"phpunit/phpunit": "^9.5"
},
"license": "MIT",
"autoload":{
Expand All @@ -32,5 +33,10 @@
"coverage": ["php -dzend_extension=xdebug.so ./vendor/bin/phpunit --coverage-text --coverage-html=build/coverage"],
"cs": ["php-cs-fixer fix -v --dry-run", "phpcs --standard=./phpcs.xml src"],
"cs-fix": ["php-cs-fixer fix -v", "phpcbf src"]
},
"config": {
"allow-plugins": {
"aura/installer-default": true
}
}
}
22 changes: 12 additions & 10 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
<phpunit bootstrap="tests/bootstrap.php">
<testsuites>
<testsuite>
<testsuite name="Ray.WebFormModule Test Suite">
<directory suffix="Test.php">tests</directory>
</testsuite>
</testsuites>

<coverage>
<include>
<directory suffix=".php">src</directory>
</include>
<report>
<html outputDirectory="build/coverage"/>
<clover outputFile="build/logs/clover.xml"/>
</report>
</coverage>

<logging>
<log type="coverage-html" target="build/coverage"/>
<log type="coverage-clover" target="build/logs/clover.xml"/>
<log type="junit" target="build/logs/junit.xml" logIncompleteSkipped="false"/>
<junit outputFile="build/logs/junit.xml"/>
</logging>

<filter>
<whitelist>
<directory suffix=".php">src</directory>
</whitelist>
</filter>
</phpunit>
81 changes: 34 additions & 47 deletions src/AbstractForm.php
Original file line number Diff line number Diff line change
@@ -1,41 +1,42 @@
<?php

declare(strict_types=1);

/**
* This file is part of the Ray.WebFormModule package.
*
* @license http://opensource.org/licenses/MIT MIT
*/

namespace Ray\WebFormModule;

use ArrayIterator;
use Aura\Filter\FilterFactory;
use Aura\Filter\SubjectFilter;
use Aura\Html\HelperLocator;
use Aura\Html\HelperLocatorFactory;
use Aura\Input\AntiCsrfInterface;
use Aura\Input\BuilderInterface;
use Aura\Input\Fieldset;
use Exception;
use Ray\Di\Di\Inject;
use Ray\Di\Di\PostConstruct;
use Ray\WebFormModule\Exception\CsrfViolationException;
use Ray\WebFormModule\Exception\LogicException;

use function trigger_error;

abstract class AbstractForm extends Fieldset implements FormInterface
{
/**
* @var SubjectFilter
*/
/** @var SubjectFilter */
protected $filter;

/**
* @var null | array
*/
/** @var array|null */
protected $errorMessages;

/**
* @var HelperLocator
*/
protected $helper;
protected HelperLocator $helper;

/**
* @var AntiCsrfInterface
*/
/** @var AntiCsrfInterface */
protected $antiCsrf;

public function __construct()
Expand All @@ -61,58 +62,51 @@ public function __toString()
}

return $this->toString();
} catch (\Exception $e) {
} catch (Exception $e) {
trigger_error($e->getMessage() . PHP_EOL . $e->getTraceAsString(), E_USER_ERROR);

return '';
}

return '';
}

/**
* @param BuilderInterface $builder
* @param FilterFactory $filterFactory
* @param HelperLocatorFactory $helperFactory
*
* @\Ray\Di\Di\Inject
*/
#[Inject]
public function setBaseDependencies(
BuilderInterface $builder,
FilterFactory $filterFactory,
HelperLocatorFactory $helperFactory
) {
): void {
$this->builder = $builder;
$this->filter = $filterFactory->newSubjectFilter();
$this->helper = $helperFactory->newInstance();
}

public function setAntiCsrf(AntiCsrfInterface $antiCsrf)
public function setAntiCsrf(AntiCsrfInterface $antiCsrf): void
{
$this->antiCsrf = $antiCsrf;
}

/**
* @\Ray\Di\Di\PostConstruct
*/
public function postConstruct()
#[PostConstruct]
public function postConstruct(): void
{
$this->init();
if ($this->antiCsrf instanceof AntiCsrfInterface) {
$this->antiCsrf->setField($this);
}
}

/**
* {@inheritdoc}
*/
/** {@inheritdoc} */
public function input($input)
{
return $this->helper->input($this->get($input));
}

/**
* {@inheritdoc}
*/
public function error($input)
/** {@inheritdoc} */
public function error(string $input): string
{
if (! $this->errorMessages) {
$failure = $this->filter->getFailures();
Expand All @@ -131,12 +125,10 @@ public function error($input)
/**
* @param array $attr attributes for the form tag
*
* @throws \Aura\Html\Exception\HelperNotFound
* @throws \Aura\Input\Exception\NoSuchInput
*
* @return string
* @throws \Aura\Html\Exception\HelperNotFound
*/
public function form($attr = [])
public function form(array $attr = []): string
{
$form = $this->helper->form($attr);
if (isset($this->inputs['__csrf_token'])) {
Expand All @@ -152,14 +144,13 @@ public function form($attr = [])
* @param array $data
*
* @throws CsrfViolationException
*
* @return bool
*/
public function apply(array $data)
public function apply(array $data): bool
{
if ($this->antiCsrf && ! $this->antiCsrf->isValid($data)) {
throw new CsrfViolationException;
}

$this->fill($data);

return $this->filter->apply($data);
Expand All @@ -168,20 +159,16 @@ public function apply(array $data)
/**
* Returns all failure messages for all fields.
*
* @return array
* @return list<string>
*/
public function getFailureMessages()
public function getFailureMessages(): array
{
return $this->filter->getFailures()->getMessages();
}
Comment on lines 159 to 167
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's locate and examine the AbstractForm.php file
find . -name "AbstractForm.php" -type f

Repository: ray-di/Ray.WebFormModule

Length of output: 90


🏁 Script executed:

# Check if there's a composer.json to understand dependencies
cat composer.json 2>/dev/null | head -50

Repository: ray-di/Ray.WebFormModule

Length of output: 1190


🏁 Script executed:

# Search for references to Aura Filter in the codebase
rg "Aura\\\\Filter" --type php -A 3

Repository: ray-di/Ray.WebFormModule

Length of output: 732


🏁 Script executed:

# Let's examine the AbstractForm.php file, particularly around lines 159-167
cat -n ./src/AbstractForm.php | sed -n '150,175p'

Repository: ray-di/Ray.WebFormModule

Length of output: 828


🏁 Script executed:

# Also, let's see the full context of the class to understand $this->filter
head -50 ./src/AbstractForm.php

Repository: ray-di/Ray.WebFormModule

Length of output: 1125


🏁 Script executed:

# Search for documentation or type hints related to SubjectFilter and getFailures()
rg "getFailures|getMessages" --type php -B 2 -A 5

Repository: ray-di/Ray.WebFormModule

Length of output: 986


🏁 Script executed:

# Let's look for the complete context where errorMessages is used to understand the structure better
cat -n ./src/AbstractForm.php | sed -n '120,150p'

Repository: ray-di/Ray.WebFormModule

Length of output: 1015


🏁 Script executed:

# Search for vendor/aura/filter to see if we can find the actual implementation
find . -path "*/vendor/aura/filter*" -type d 2>/dev/null | head -5

Repository: ray-di/Ray.WebFormModule

Length of output: 50


🏁 Script executed:

# Let's check if there's any test file that shows the expected return type
rg "getFailureMessages|getMessages" --type php -B 5 -A 10 tests/

Repository: ray-di/Ray.WebFormModule

Length of output: 50


🌐 Web query:

Aura Filter getMessages() return type documentation

💡 Result:

In Aura.Filter 2.x, SubjectFilter::getFailures() returns a FailureCollection (described as “essentially an ArrayObject keyed on the field names”). Each field entry contains an array of Failure objects, where each Failure has getMessage() (the failure message). The FailureCollection::getMessages() method is used to extract those into a messages array. [1]

So the effective return type of FailureCollection::getMessages() is:

  • array<string, array<int, string>> (aka “array keyed by field name, each value an array of message strings”). This is the same “shape” shown in the Aura docs’ getMessages() examples (field => list of messages). [2]

Sources:
[1] Aura.Filter 2.x “Filtering Arrays and Objects” (FailureCollection + getMessages() usage) https://auraphp.com/packages/2.x/Filter/subject-filter.html
[2] Aura framework validation docs showing getMessages() output structure https://auraphp.com/framework/2.x/en/validation/


🌐 Web query:

Aura Filter FailureCollection getMessages() PHP

💡 Result:

In Aura.Filter 2.2+, SubjectFilter::getMessages() was removed and replaced by failures reporting via getFailures() (a FailureCollection). (php.libhunt.com)

Use:

$success = $filter->apply($subject);

if (! $success) {
    $failures = $filter->getFailures();          // FailureCollection
    $messages = $failures->getMessages();        // array of messages (keyed by field)
}

This is the pattern shown in the Aura.Filter docs; getFailures() returns a FailureCollection keyed by field name, and getMessages() gives you the failure messages. (auraphp.com)

Citations:


Incorrect @return docblock — getMessages() returns an associative array, not list<string>.

Aura\Filter\SubjectFilter::getFailures()->getMessages() returns an array keyed by field name with arrays of message strings. The list<string> annotation is incorrect.

Proposed fix
-     * `@return` list<string>
+     * `@return` array<string, array<string>>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Returns all failure messages for all fields.
*
* @return array
* @return list<string>
*/
public function getFailureMessages()
public function getFailureMessages(): array
{
return $this->filter->getFailures()->getMessages();
}
/**
* Returns all failure messages for all fields.
*
* `@return` array<string, array<string>>
*/
public function getFailureMessages(): array
{
return $this->filter->getFailures()->getMessages();
}
🤖 Prompt for AI Agents
In `@src/AbstractForm.php` around lines 159 - 167, The docblock for
getFailureMessages() is wrong: getFailures()->getMessages() returns an
associative array keyed by field name with arrays of message strings, not a
list; update the PHPDoc return annotation in AbstractForm::getFailureMessages()
from "list<string>" to a keyed array type such as "array<string, list<string>>"
or "array<string, string[]>" (e.g. array<string, string[]>) to accurately
reflect the shape returned by getFailures()->getMessages(); keep the method
signature as is.


/**
* Returns all the fields collection
*
* @return \ArrayIterator
*/
public function getIterator()
/** Returns all the fields collection */
public function getIterator(): ArrayIterator
{
return new \ArrayIterator($this->inputs);
return new ArrayIterator($this->inputs);
}
}
Loading