kariricode/property-inspector
Composer 安装命令:
composer require kariricode/property-inspector
包简介
Attribute-based property analysis and inspection for the KaririCode Framework, enabling dynamic validation, normalization, and processing of object properties via PHP 8.4+ attributes and reflection.
关键字:
README 文档
README
Attribute-based property analysis and inspection for the KaririCode Framework —
multi-pass pipelines, reflection caching, and zero-overhead property mutation, PHP 8.4+.
Installation · Quick Start · Features · Pipeline · Architecture
The Problem
PHP reflection is boilerplate-heavy, error-prone, and slow when repeated across object graphs:
// The old way: raw reflection on every request $ref = new ReflectionClass($user); foreach ($ref->getProperties() as $prop) { $attrs = $prop->getAttributes(Validate::class); foreach ($attrs as $attr) { $prop->setAccessible(true); // deprecated in PHP 8.4 $value = $prop->getValue($user); // now what? where does the result go? how do you write it back? } }
No caching, no mutation abstraction, no error isolation, no handler contract — just raw loops you repeat in every project.
The Solution
use KaririCode\PropertyInspector\AttributeAnalyzer; use KaririCode\PropertyInspector\Utility\PropertyInspector; use KaririCode\PropertyInspector\Utility\PropertyAccessor; // 1. Configure which attribute to scan for $analyzer = new AttributeAnalyzer(Validate::class); $inspector = new PropertyInspector($analyzer); // 2. Inspect — results cached after first call per class $handler = new MyValidationHandler(); $inspector->inspect($user, $handler); // 3. Read processed values and errors $values = $handler->getProcessedPropertyValues(); $errors = $handler->getProcessingResultErrors(); // 4. Write back changed values via PropertyAccessor $accessor = new PropertyAccessor($user, 'email'); $accessor->setValue(strtolower($accessor->getValue()));
Requirements
| Requirement | Version |
|---|---|
| PHP | 8.4 or higher |
| kariricode/contract | ^2.8 |
| kariricode/exception | ^1.2 |
Installation
composer require kariricode/property-inspector
Quick Start
Define an attribute, an entity, a handler — and inspect:
<?php declare(strict_types=1); require_once __DIR__ . '/vendor/autoload.php'; use Attribute; use KaririCode\PropertyInspector\AttributeAnalyzer; use KaririCode\PropertyInspector\Contract\PropertyAttributeHandler; use KaririCode\PropertyInspector\Utility\PropertyInspector; // 1. Define a custom attribute #[Attribute(Attribute::TARGET_PROPERTY)] final class Validate { public function __construct(public readonly array $rules = []) {} } // 2. Define an entity with annotated properties final class User { public function __construct( #[Validate(['required', 'min:3'])] public string $name = '', #[Validate(['required', 'email'])] public string $email = '', #[Validate(['required', 'min:18'])] public int $age = 0, ) {} } // 3. Implement a handler final class ValidationHandler implements PropertyAttributeHandler { private array $processed = []; private array $errors = []; public function handleAttribute(string $propertyName, object $attribute, mixed $value): mixed { $this->processed[$propertyName] = $value; if ($attribute instanceof Validate) { foreach ($attribute->rules as $rule) { if ($rule === 'required' && ($value === '' || $value === null)) { $this->errors[$propertyName]['required'] = 'Field is required'; } if (str_starts_with($rule, 'min:')) { $min = (int) substr($rule, 4); if (is_string($value) && strlen($value) < $min) { $this->errors[$propertyName]['min'] = "Min {$min} chars required"; } if (is_int($value) && $value < $min) { $this->errors[$propertyName]['min'] = "Must be at least {$min}"; } } } } return $value; } public function getProcessedPropertyValues(): array { return $this->processed; } public function getProcessingResultMessages(): array { return []; } public function getProcessingResultErrors(): array { return $this->errors; } } // 4. Run the pipeline $user = new User(name: 'Walmir', email: 'walmir@kariricode.org', age: 30); $analyzer = new AttributeAnalyzer(Validate::class); $inspector = new PropertyInspector($analyzer); $handler = new ValidationHandler(); $inspector->inspect($user, $handler); var_dump($handler->getProcessedPropertyValues()); // ['name' => 'Walmir', 'email' => 'walmir@kariricode.org', 'age' => 30] var_dump($handler->getProcessingResultErrors()); // [] — all good
Features
Reflection Caching
AttributeAnalyzer caches reflection metadata after the first analysis per class. Subsequent calls for the same class — even with different object instances — skip ReflectionClass entirely:
$analyzer = new AttributeAnalyzer(Validate::class); $inspector = new PropertyInspector($analyzer); // First call: reflection + cache build $inspector->inspect($user1, $handler1); // Subsequent calls: metadata from cache — zero reflection overhead $inspector->inspect($user2, $handler2); $inspector->inspect($user3, $handler3); // Force re-analysis when needed (e.g., after metadata change) $analyzer->clearCache();
Multi-Pass Inspection
Run multiple independent passes over the same object with different attribute types:
// Pass 1: sanitize $sanitizeInspector = new PropertyInspector(new AttributeAnalyzer(Sanitize::class)); $sanitizeHandler = new TrimLowercaseHandler(); $sanitizeInspector->inspect($user, $sanitizeHandler); // Apply sanitized values back to the object foreach ($sanitizeHandler->getProcessedPropertyValues() as $prop => $value) { (new PropertyAccessor($user, $prop))->setValue($value); } // Pass 2: validate on sanitized data $validateInspector = new PropertyInspector(new AttributeAnalyzer(Validate::class)); $validateHandler = new ValidationHandler(); $validateInspector->inspect($user, $validateHandler); $errors = $validateHandler->getProcessingResultErrors(); // [] if clean
PropertyAccessor — Safe Property Mutation
Read and write any property (public, protected, private) without setAccessible boilerplate:
use KaririCode\PropertyInspector\Utility\PropertyAccessor; $accessor = new PropertyAccessor($user, 'email'); $current = $accessor->getValue(); // read $accessor->setValue(strtolower($current)); // write (no setAccessible needed)
Attribute Polymorphism
AttributeAnalyzer uses ReflectionAttribute::IS_INSTANCEOF — it matches attribute hierarchies, not just exact class names:
// Matches Validate + any subclass of Validate $analyzer = new AttributeAnalyzer(Validate::class);
Isolated Error Handling
All exceptions from reflection or handler code are wrapped in PropertyInspectionException — your calling code only needs to catch one type:
use KaririCode\PropertyInspector\Exception\PropertyInspectionException; try { $inspector->inspect($user, $handler); } catch (PropertyInspectionException $e) { // ReflectionException, TypeError, Error — all caught and re-wrapped }
The Inspection Pipeline
$inspector->inspect($object, $handler)
│
▼
AttributeAnalyzer::analyzeObject($object)
├── Check class cache
├── If miss: ReflectionClass → getProperties()
│ └── foreach property:
│ getAttributes($attributeClass, IS_INSTANCEOF)
│ newInstance() → cache [{attributes, property}]
└── extractValues($object): [{value, attributes}]
│
▼
foreach property → foreach attribute:
$handler->handleAttribute($propertyName, $attribute, $value)
│
▼
return $handler (accumulates processed values + errors)
Architecture
Source layout
src/
├── AttributeAnalyzer.php Core analyzer — reflection + cache + attribute extraction
├── Contract/
│ ├── AttributeAnalyzer.php Interface: analyzeObject · clearCache
│ ├── PropertyAttributeHandler.php Interface: handleAttribute · getProcessed* · getErrors
│ ├── PropertyChangeApplier.php Interface: applyChanges
│ └── PropertyInspector.php Interface: inspect
├── Exception/
│ └── PropertyInspectionException.php Named factory methods per failure mode
└── Utility/
├── PropertyAccessor.php Safe property read/write (private, protected, public)
└── PropertyInspector.php Orchestrator: delegates analysis → handler
Key design decisions
| Decision | Rationale | ADR |
|---|---|---|
| Reflection cache per class | One ReflectionClass call per type, not per instance |
— |
Remove setAccessible |
Deprecated in PHP 8.1, removed in PHP 9; PropertyAccessor handles this |
ADR-001 |
clearCache() on interface |
Enables test isolation and dynamic class reloading | ADR-002 |
| Wrapped exception hierarchy | Callers catch PropertyInspectionException, not reflection internals |
ADR-003 |
| Handler-returned values | Handler decides the processed value — supports chaining and transformation | — |
Specifications
| Spec | Covers |
|---|---|
| SPEC-001 | Full pipeline: analysis → handler → mutation |
Integration with the KaririCode Ecosystem
PropertyInspector is the reflection engine used internally by other KaririCode components:
| Component | Role |
|---|---|
kariricode/validator |
Uses PropertyInspector to discover #[Rule] attributes and dispatch to rule processors |
kariricode/sanitizer |
Uses PropertyInspector to discover #[Sanitize] attributes and apply transformers |
kariricode/normalizer |
Uses PropertyInspector for attribute-driven normalization passes |
Any component that needs declarative, attribute-based property processing can be built on top of this pipeline.
Project Stats
| Metric | Value |
|---|---|
| PHP source files | 7 |
| External runtime dependencies | 2 (contract · exception) |
| Test suite | 40 tests · 96 assertions |
| PHPStan level | 9 |
| PHP version | 8.4+ |
| ARFA compliance | 1.3 |
| Test suites | Unit + Integration |
| Reflection cache | Per-class, per-AttributeAnalyzer instance |
Contributing
git clone https://github.com/KaririCode-Framework/kariricode-property-inspector.git cd kariricode-property-inspector composer install kcode init kcode quality # Must pass before opening a PR
License
Part of the KaririCode Framework ecosystem.
kariricode.org · GitHub · Packagist · Issues
kariricode/property-inspector 适用场景与选型建议
kariricode/property-inspector 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 1.09k 次下载、GitHub Stars 达 0, 最近一次更新时间为 2024 年 10 月 14 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「framework」 「metadata」 「reflection」 「validation」 「attribute」 「inspection」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 kariricode/property-inspector 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 kariricode/property-inspector 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 kariricode/property-inspector 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
PHP Framework HLEB2 is the foundation of the web application. Provides ease of development and application performance.
Library emulating the PHP internal reflection using just the tokenized source code.
Parse use statements for a reflection object
Generate UML class diagrams by reflection for your PHP projects
Yii2 Component to manage SEO data and metadata
Extremely simple meta tag generator.
统计信息
- 总下载量: 1.09k
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 23
- 依赖项目数: 4
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2024-10-14