PHPackages                             tommyknocker/struct - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [PSR &amp; Standards](/categories/psr-standards)
4. /
5. tommyknocker/struct

ActiveLibrary[PSR &amp; Standards](/categories/psr-standards)

tommyknocker/struct
===================

PHP Structure helper with typed properties and attributes

v1.2.0(6mo ago)28MITPHPPHP ^8.1CI failing

Since Nov 15Pushed 6mo ago1 watchersCompare

[ Source](https://github.com/tommyknocker/struct)[ Packagist](https://packagist.org/packages/tommyknocker/struct)[ RSS](/packages/tommyknocker-struct/feed)WikiDiscussions master Synced 4d ago

READMEChangelog (2)Dependencies (4)Versions (7)Used By (0)

Struct
======

[](#struct)

[![CI](https://github.com/tommyknocker/struct/workflows/CI/badge.svg)](https://github.com/tommyknocker/struct/actions)[![PHPStan Level 9](https://camo.githubusercontent.com/1bc07920f0d36e55c17e1d38b1caa132cc605f51a82b388c962870b9a747b898/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d6c6576656c253230392d627269676874677265656e2e737667)](https://phpstan.org/)[![PHP Version](https://camo.githubusercontent.com/7535257ca228724c93658bd52583d4e47a9bab02c356abf6e54c1d575f2151e6/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e312532422d626c75652e737667)](https://www.php.net/)[![License: MIT](https://camo.githubusercontent.com/fdf2982b9f5d7489dcf44570e714e3a15fce6253e0cc6b5aa61a075aac2ff71b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d79656c6c6f772e737667)](https://opensource.org/licenses/MIT)

A lightweight, type-safe structure helper for PHP 8.1+.
Define your data models with attributes, get automatic validation, array access, and JSON serialization.

---

🚀 Why Struct?
-------------

[](#-why-struct)

Instead of manually validating arrays, you can define a strict data model with attributes. This makes your code:

- ✅ **Type-safe** with runtime validation
- 🔒 **Immutable** with readonly properties
- 📦 **Serializable** with built-in JSON support
- 🎯 **Simple** with minimal boilerplate

---

📦 Installation
--------------

[](#-installation)

Install via Composer:

```
composer require tommyknocker/struct
```

**Requirements:**

- PHP 8.1 or higher
- Composer

---

✨ Features
----------

[](#-features)

- 🏷️ **Attribute-based field definitions** – Clean and declarative syntax
- ✅ **Advanced type validation** – Scalars, objects, arrays, enums, DateTime, union types
- 🔒 **Immutability** – readonly properties by design
- 🌐 **JSON support** – `toJson()`, `fromJson()`, `JsonSerializable`
- 🔄 **Array conversion** – `toArray()` with recursive support
- 📝 **Default values** – Optional fields with defaults
- 🔑 **Field aliases** – Map different key names
- ✔️ **Flexible validation system** – Custom validators, validation rules, and transformers
- 🎭 **Mixed type support** – Handle dynamic data
- ⏰ **DateTime parsing** – Automatic string to DateTime conversion
- 🔁 **Cloning with modifications** – `with()` method
- 📊 **ArrayAccess** – Array-like read access
- 🧰 **PSR-11 container integration** – DI support
- 🏭 **Factory pattern** – Centralized struct creation with dependency injection
- 🔍 **PHPStan Level 9** – Maximum static analysis
- 🧪 **100% tested** – PHPUnit coverage
- ⚡ **Performance optimized** – Reflection caching and metadata system
- 🛠️ **Attribute Helper** – Automatic Field attribute generation with intelligent type inference

🎯 Use Cases
-----------

[](#-use-cases)

Perfect for:

- 📱 **REST API validation** for mobile apps with flexible field types
- 🔄 **Data Transfer Objects (DTOs)** in clean architecture with validation rules
- 🌐 **Third-party API integration** with field mapping and transformations
- ✅ **Form validation** with complex rules and data processing
- 📊 **Data serialization/deserialization** with custom formats
- 🛡️ **Type-safe data handling** in microservices with union types
- 🏭 **Enterprise applications** with centralized struct creation and dependency injection
- 🔍 **Data processing pipelines** with automatic transformations and validation

👉 **[See practical examples](examples/)** for mobile app REST API scenarios

---

📚 Examples
----------

[](#-examples)

### Basic Usage: Scalars

[](#basic-usage-scalars)

```
use tommyknocker\struct\Struct;
use tommyknocker\struct\Field;

final class Hit extends Struct
{
    #[Field('string')]
    public readonly string $date;

    #[Field('int')]
    public readonly int $type;

    #[Field('string')]
    public readonly string $ip;

    #[Field('string')]
    public readonly string $uuid;

    #[Field('string')]
    public readonly string $referer;
}

$hit = new Hit([
    'date' => '2025-10-09',
    'type' => 1,
    'ip' => '127.0.0.1',
    'uuid' => '7185bbe3-cdd7-4154-88c3-c63416a76327',
    'referer' => 'https://google.com',
]);

echo $hit->date; // 2025-10-09
echo $hit['ip']; // 127.0.0.1 (ArrayAccess support)
```

### Nullable Fields

[](#nullable-fields)

```
final class Person extends Struct
{
    #[Field('string')]
    public readonly string $name;

    #[Field('int', nullable: true)]
    public readonly ?int $age;
}

$person = new Person(['name' => 'Alice', 'age' => null]);
```

### Default Values

[](#default-values)

```
final class Config extends Struct
{
    #[Field('string', default: 'localhost')]
    public readonly string $host;

    #[Field('int', default: 3306)]
    public readonly int $port;
}

// Both fields use defaults
$config = new Config([]);
echo $config->host; // localhost
echo $config->port; // 3306
```

### Field Aliases

[](#field-aliases)

```
final class User extends Struct
{
    #[Field('string', alias: 'user_name')]
    public readonly string $name;

    #[Field('string', alias: 'email_address')]
    public readonly string $email;
}

// Use API keys as they come
$user = new User([
    'user_name' => 'John',
    'email_address' => 'john@example.com'
]);
```

### Union Types

[](#union-types)

```
final class FlexibleField extends Struct
{
    #[Field(['string', 'int'])]
    public readonly string|int $value;
}

$flexible = new FlexibleField(['value' => 'hello']); // ✅ String
$flexible2 = new FlexibleField(['value' => 42]);     // ✅ Integer
// new FlexibleField(['value' => 3.14]); // ❌ Float not allowed
```

### Advanced Validation Rules

[](#advanced-validation-rules)

```
use tommyknocker\struct\validation\rules\EmailRule;
use tommyknocker\struct\validation\rules\RangeRule;

final class UserProfile extends Struct
{
    #[Field('string', validationRules: [new EmailRule()])]
    public readonly string $email;

    #[Field('int', validationRules: [new RangeRule(18, 120)])]
    public readonly int $age;
}

$profile = new UserProfile([
    'email' => 'user@example.com',
    'age' => 25
]); // ✅ Valid
```

### Data Transformations

[](#data-transformations)

```
use tommyknocker\struct\transformation\StringToUpperTransformer;

final class ProcessedData extends Struct
{
    #[Field('string', transformers: [new StringToUpperTransformer()])]
    public readonly string $name;
}

$data = new ProcessedData(['name' => 'john doe']);
echo $data->name; // JOHN DOE
```

### Custom Validation (Legacy Support)

[](#custom-validation-legacy-support)

```
class EmailValidator
{
    public static function validate(mixed $value): bool|string
    {
        if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
            return "Invalid email format";
        }
        return true;
    }
}

final class Contact extends Struct
{
    #[Field('string', validator: EmailValidator::class)]
    public readonly string $email;
}

$contact = new Contact(['email' => 'test@example.com']); // ✅ OK
// new Contact(['email' => 'invalid']); // ❌ Throws ValidationException
```

### DateTime Support

[](#datetime-support)

```
final class Event extends Struct
{
    #[Field('string')]
    public readonly string $name;

    #[Field(\DateTimeImmutable::class)]
    public readonly \DateTimeImmutable $date;
}

// Accepts string or DateTime
$event = new Event([
    'name' => 'Conference',
    'date' => '2025-12-31 10:00:00'
]);
```

### Mixed Type Support

[](#mixed-type-support)

```
final class Payload extends Struct
{
    #[Field('string')]
    public readonly string $type;

    #[Field('mixed')]
    public readonly mixed $data; // Can be anything
}
```

### Nested Objects

[](#nested-objects)

```
final class Address extends Struct
{
    #[Field('string')]
    public readonly string $city;

    #[Field('string')]
    public readonly string $street;
}

final class User extends Struct
{
    #[Field('string')]
    public readonly string $name;

    #[Field(Address::class)]
    public readonly Address $address;
}

$user = new User([
    'name' => 'Bob',
    'address' => ['city' => 'Berlin', 'street' => 'Unter den Linden'],
]);
```

### Arrays of Objects

[](#arrays-of-objects)

```
final class UserWithHistory extends Struct
{
    #[Field('string')]
    public readonly string $name;

    #[Field(Address::class, isArray: true)]
    public readonly array $previousAddresses;
}

$user = new UserWithHistory([
    'name' => 'Charlie',
    'previousAddresses' => [
        ['city' => 'Paris', 'street' => 'Champs-Élysées'],
        ['city' => 'Rome', 'street' => 'Via del Corso'],
    ],
]);
```

### Enums

[](#enums)

```
enum UserType: string
{
    case Admin = 'admin';
    case Regular = 'regular';
    case Guest = 'guest';
}

final class Account extends Struct
{
    #[Field(UserType::class)]
    public readonly UserType $type;

    #[Field('string')]
    public readonly string $email;
}

$account = new Account([
    'type' => UserType::Admin,
    'email' => 'admin@example.com',
]);
```

### JSON Serialization

[](#json-serialization)

```
$user = new User(['name' => 'Alice', 'address' => ['city' => 'Berlin', 'street' => 'Main St']]);

// To JSON
$json = $user->toJson(pretty: true);

// From JSON
$restored = User::fromJson($json);

// To Array
$array = $user->toArray(); // Recursive for nested structs
```

### Cloning with Modifications

[](#cloning-with-modifications)

```
$user = new User(['name' => 'Alice', 'age' => 30]);

// Create modified copy
$updated = $user->with(['age' => 31]);

echo $user->age;    // 30 (original unchanged)
echo $updated->age; // 31 (new instance)
```

### Strict Mode (Validate No Extra Fields)

[](#strict-mode-validate-no-extra-fields)

```
use tommyknocker\struct\Struct;

// Enable strict mode globally
Struct::$strictMode = true;

final class ApiRequest extends Struct
{
    #[Field('string')]
    public readonly string $username;

    #[Field('string')]
    public readonly string $email;
}

// ✅ Valid - all fields are known
$request = new ApiRequest([
    'username' => 'john',
    'email' => 'john@example.com',
]);

// ❌ Throws RuntimeException: Unknown field: extra_field
$request = new ApiRequest([
    'username' => 'john',
    'email' => 'john@example.com',
    'extra_field' => 'not allowed!',
]);

// Disable strict mode (default behavior - extra fields ignored)
Struct::$strictMode = false;
```

### Factory Pattern

[](#factory-pattern)

```
use tommyknocker\struct\factory\StructFactory;

// Setup factory with dependencies
$factory = new StructFactory();

// Create struct instances
$user = $factory->create(User::class, [
    'name' => 'Alice',
    'email' => 'alice@example.com'
]);

// Create from JSON
$userFromJson = $factory->createFromJson(User::class, '{"name":"Bob","email":"bob@example.com"}');
```

### Error Handling

[](#error-handling)

```
use tommyknocker\struct\exception\ValidationException;
use tommyknocker\struct\exception\FieldNotFoundException;

try {
    $user = new User(['name' => 'John', 'email' => 'invalid-email']);
} catch (ValidationException $e) {
    echo "Validation error: " . $e->getMessage();
    echo "Field: " . $e->fieldName;
    echo "Value: " . $e->value;
} catch (FieldNotFoundException $e) {
    echo "Missing field: " . $e->getMessage();
}
```

### Real-World API Example

[](#real-world-api-example)

```
// API endpoint for user registration
final class RegisterRequest extends Struct
{
    #[Field('string', validationRules: [new EmailRule()])]
    public readonly string $email;

    #[Field('string', validationRules: [new RangeRule(8, 50)])]
    public readonly string $password;

    #[Field('string', alias: 'full_name')]
    public readonly string $fullName;

    #[Field('int', nullable: true, validationRules: [new RangeRule(13, 120)])]
    public readonly ?int $age;
}

// In your API controller
public function register(Request $request): JsonResponse
{
    try {
        $data = RegisterRequest::fromJson($request->getContent());

        // Create user account
        $user = User::create([
            'email' => $data->email,
            'password' => Hash::make($data->password),
            'full_name' => $data->fullName,
            'age' => $data->age,
        ]);

        return response()->json([
            'success' => true,
            'user' => $user->toArray()
        ]);

    } catch (ValidationException $e) {
        return response()->json([
            'success' => false,
            'error' => $e->getMessage(),
            'field' => $e->fieldName
        ], 422);
    }
}
```

---

🛠️ Attribute Helper
-------------------

[](#️-attribute-helper)

The Attribute Helper automatically generates `Field` attributes for your Struct classes, reducing boilerplate code by up to 80% and ensuring consistent patterns across your codebase.

### Why Use Attribute Helper?

[](#why-use-attribute-helper)

- ✅ **Reduces boilerplate** – No more manual attribute writing
- ✅ **Intelligent suggestions** – Smart defaults based on property names and types
- ✅ **Consistent patterns** – Ensures uniform attribute usage
- ✅ **Error prevention** – Prevents typos and missing attributes
- ✅ **Rapid development** – Generate attributes for entire projects in seconds

### Console Usage

[](#console-usage)

```
# Generate attributes for a single file
php scripts/struct-helper.php src/UserProfile.php

# Generate attributes for entire directory
php scripts/struct-helper.php src/

# Dry run (see what would be changed)
php scripts/struct-helper.php --dry-run src/

# Verbose output
php scripts/struct-helper.php --verbose src/

# Don't create backup files
php scripts/struct-helper.php --no-backup src/
```

### Before and After

[](#before-and-after)

**Before (Manual):**

```
final class UserProfile extends Struct
{
    #[Field('string', validationRules: [new RequiredRule()], transformers: [new StringToUpperTransformer()])]
    public readonly string $firstName;

    #[Field('string', validationRules: [new RequiredRule()], transformers: [new StringToUpperTransformer()])]
    public readonly string $lastName;

    #[Field('string', validationRules: [new EmailRule()], transformers: [new StringToLowerTransformer()])]
    public readonly string $emailAddress;

    #[Field('string', nullable: true, alias: 'phone_number')]
    public readonly ?string $phoneNumber;

    #[Field('int', validationRules: [new RangeRule(13, 120)])]
    public readonly int $age;
}
```

**After (Auto-generated):**

```
final class UserProfile extends Struct
{
    public readonly string $firstName;
    public readonly string $lastName;
    public readonly string $emailAddress;
    public readonly ?string $phoneNumber;
    public readonly int $age;
}
```

Run the helper and it automatically generates:

```
#[Field('string', validationRules: [new RequiredRule()], transformers: [new StringToUpperTransformer()])]
public readonly string $firstName;

#[Field('string', validationRules: [new RequiredRule()], transformers: [new StringToUpperTransformer()])]
public readonly string $lastName;

#[Field('string', validationRules: [new EmailRule()], transformers: [new StringToLowerTransformer()])]
public readonly string $emailAddress;

#[Field('string', nullable: true, alias: 'phone_number')]
public readonly ?string $phoneNumber;

#[Field('int', validationRules: [new RangeRule(13, 120)])]
public readonly int $age;
```

### Intelligent Features

[](#intelligent-features)

#### Automatic Type Inference

[](#automatic-type-inference)

```
public readonly string $name;        // → #[Field('string')]
public readonly int $age;            // → #[Field('int')]
public readonly ?string $email;      // → #[Field('string', nullable: true)]
public readonly array $tags;         // → #[Field('array', isArray: true)]
public readonly string|int $value;   // → #[Field(['string', 'int'])]
```

#### Smart Validation Rules

[](#smart-validation-rules)

```
public readonly string $email;       // → validationRules: [new EmailRule()]
public readonly string $password;    // → validationRules: [new RequiredRule()]
public readonly int $age;            // → validationRules: [new RangeRule(1, 100)]
public readonly int $score;          // → validationRules: [new RangeRule(1, 100)]
```

#### Automatic Field Aliases

[](#automatic-field-aliases)

```
public readonly string $firstName;   // → alias: 'first_name'
public readonly string $emailAddress; // → alias: 'email_address'
public readonly string $phoneNumber; // → alias: 'phone_number'
public readonly string $createdAt;  // → alias: 'created_at'
```

#### Smart Transformations

[](#smart-transformations)

```
public readonly string $email;       // → transformers: [new StringToLowerTransformer()]
public readonly string $username;    // → transformers: [new StringToLowerTransformer()]
public readonly string $name;        // → transformers: [new StringToUpperTransformer()]
public readonly string $title;      // → transformers: [new StringToUpperTransformer()]
```

#### Intelligent Defaults

[](#intelligent-defaults)

```
public readonly bool $isEnabled;     // → default: true
public readonly bool $isActive;      // → default: true
public readonly int $port;           // → default: 3306
public readonly string $host;        // → default: 'localhost'
public readonly array $items;        // → default: []
```

### Programmatic Usage

[](#programmatic-usage)

```
use tommyknocker\struct\tools\AttributeHelper;

$helper = new AttributeHelper();

// Generate attribute for a single property
$property = new ReflectionProperty(MyStruct::class, 'email');
$attribute = $helper->generateFieldAttribute($property);
echo $attribute; // #[Field('string', validationRules: [new EmailRule()], transformers: [new StringToLowerTransformer()])]

// Process entire class
$attributes = $helper->processClass(MyStruct::class);
foreach ($attributes as $propertyName => $attribute) {
    echo "{$propertyName}: {$attribute}\n";
}

// Get properties that need attributes
$properties = $helper->getPropertiesNeedingAttributes(MyStruct::class);
foreach ($properties as $property) {
    echo "Property {$property->getName()} needs an attribute\n";
}
```

### Real-World Example

[](#real-world-example)

```
// API Integration Scenario
final class ProductApiResponse extends Struct
{
    public readonly string $productId;
    public readonly string $productName;
    public readonly float $price;
    public readonly ?string $description;
    public readonly array $categories;
    public readonly bool $isAvailable;
    public readonly string $createdAt;
    public readonly string $updatedAt;
}

// Run: php scripts/struct-helper.php ProductApiResponse.php
// Generates all necessary attributes automatically!
```

### Error Handling

[](#error-handling-1)

```
use tommyknocker\struct\tools\exception\AttributeHelperException;
use tommyknocker\struct\tools\exception\FileProcessingException;
use tommyknocker\struct\tools\exception\ClassProcessingException;

try {
    $helper = new AttributeHelper();
    $attributes = $helper->processClass('MyClass');
} catch (ClassProcessingException $e) {
    echo "Failed to process class: {$e->getMessage()}";
} catch (AttributeHelperException $e) {
    echo "Attribute generation failed: {$e->getMessage()}";
}
```

👉 **[See attribute helper examples](examples/09_attribute_helper_demo.php)** for detailed demonstrations

---

💡 Best Practices
----------------

[](#-best-practices)

### 1. Always Validate Input

[](#1-always-validate-input)

```
// ✅ Good - Validate all incoming data
$userData = UserRequest::fromJson($request->getContent());

// ❌ Bad - Trusting raw input
$userData = json_decode($request->getContent(), true);
```

### 2. Use Specific Exception Types

[](#2-use-specific-exception-types)

```
try {
    $data = MyStruct::fromJson($json);
} catch (ValidationException $e) {
    // Handle validation errors specifically
    return response()->json(['error' => $e->getMessage()], 422);
} catch (FieldNotFoundException $e) {
    // Handle missing fields
    return response()->json(['error' => 'Missing required field'], 400);
}
```

### 3. Leverage Field Aliases for API Integration

[](#3-leverage-field-aliases-for-api-integration)

```
final class ApiResponse extends Struct
{
    #[Field('string', alias: 'user_name')]
    public readonly string $userName;

    #[Field('string', alias: 'email_address')]
    public readonly string $emailAddress;
}

// Works with external API that uses snake_case
$response = new ApiResponse([
    'user_name' => 'John Doe',
    'email_address' => 'john@example.com'
]);
```

### 4. Use Default Values for Optional Fields

[](#4-use-default-values-for-optional-fields)

```
final class Config extends Struct
{
    #[Field('string', default: 'localhost')]
    public readonly string $host;

    #[Field('int', default: 3306)]
    public readonly int $port;

    #[Field('bool', default: false)]
    public readonly bool $debug;
}

// All fields get defaults if not provided
$config = new Config([]);
```

### 5. Combine Validation Rules for Complex Logic

[](#5-combine-validation-rules-for-complex-logic)

```
final class PasswordField extends Struct
{
    #[Field('string', validationRules: [
        new RequiredRule(),
        new RangeRule(8, 128),
        new PasswordStrengthRule()
    ])]
    public readonly string $password;
}
```

---

❓ FAQ
-----

[](#-faq)

### Q: How is this different from regular PHP classes?

[](#q-how-is-this-different-from-regular-php-classes)

A: Struct provides automatic validation, type casting, JSON serialization, and immutability out of the box. Regular classes require manual implementation of these features.

### Q: Can I use this with existing frameworks?

[](#q-can-i-use-this-with-existing-frameworks)

A: Yes! Struct works with any PHP framework. See the [examples](examples/) for Laravel, Symfony, and Slim integration.

### Q: What about performance?

[](#q-what-about-performance)

A: Struct uses reflection caching and optimized metadata systems. It's designed for production use with minimal overhead.

### Q: Can I extend Struct classes?

[](#q-can-i-extend-struct-classes)

A: Yes, but remember that Struct classes are immutable. Use the `with()` method to create modified copies.

### Q: How do I handle optional fields?

[](#q-how-do-i-handle-optional-fields)

A: Use `nullable: true` for fields that can be null, or `default: value` for fields with default values.

### Q: What validation rules are available?

[](#q-what-validation-rules-are-available)

A: Built-in rules include `EmailRule`, `RangeRule`, `RequiredRule`. You can create custom rules by extending `ValidationRule`.

### Q: Can I use this for database models?

[](#q-can-i-use-this-for-database-models)

A: Struct is designed for data validation and transfer, not ORM functionality. Use it for DTOs, API requests/responses, and data validation.

---

🧪 Testing
---------

[](#-testing)

The library is thoroughly tested with 100% code coverage:

```
composer test
```

All examples are verified to work:

```
composer test-examples
```

---

🛠️ Development
--------------

[](#️-development)

This project follows PSR-12 coding standards and uses PHPStan Level 9 for static analysis.

For contributors:

- Run `composer check` to verify all tests and standards
- Follow the existing code style
- Add tests for new features
- Update documentation as needed

---

📝 API Reference
---------------

[](#-api-reference)

### Field Attribute

[](#field-attribute)

```
#[Field(
    type: string|array,                    // Type: 'string', 'int', 'float', 'bool', 'mixed', class-string, or array of types for union
    nullable: bool = false,                        // Allow null values
    isArray: bool = false,                         // Field is array of type
    default: mixed = null,                         // Default value if not provided
    alias: ?string = null,                         // Alternative key name in input data
    validator: ?string = null,                     // Legacy validator class with static validate() method
    validationRules: array = [],                   // Array of ValidationRule instances
    transformers: array = []                       // Array of TransformerInterface instances
)]
```

### Validation Rules

[](#validation-rules)

```
// Built-in validation rules
use tommyknocker\struct\validation\rules\EmailRule;
use tommyknocker\struct\validation\rules\RangeRule;
use tommyknocker\struct\validation\rules\RequiredRule;

// Custom validation rule
class CustomRule extends \tommyknocker\struct\validation\ValidationRule
{
    public function validate(mixed $value): \tommyknocker\struct\validation\ValidationResult
    {
        // Your validation logic
        return \tommyknocker\struct\validation\ValidationResult::valid();
    }
}
```

### Data Transformers

[](#data-transformers)

```
// Built-in transformers
use tommyknocker\struct\transformation\StringToUpperTransformer;
use tommyknocker\struct\transformation\StringToLowerTransformer;

// Custom transformer
class CustomTransformer implements \tommyknocker\struct\transformation\TransformerInterface
{
    public function transform(mixed $value): mixed
    {
        // Your transformation logic
        return $value;
    }
}
```

### Factory and Serialization

[](#factory-and-serialization)

```
// Factory for struct creation
use tommyknocker\struct\factory\StructFactory;

// JSON serialization
use tommyknocker\struct\serialization\JsonSerializer;

// Metadata system
use tommyknocker\struct\metadata\MetadataFactory;
use tommyknocker\struct\metadata\StructMetadata;
use tommyknocker\struct\metadata\FieldMetadata;
```

### Struct Methods

[](#struct-methods)

```
// Constructor
public function __construct(array $data)

// Array conversion (recursive)
public function toArray(): array

// JSON serialization
public function toJson(bool $pretty = false, int $flags = ...): string

// Create from JSON
public static function fromJson(string $json, int $flags = JSON_THROW_ON_ERROR): static

// Clone with modifications
public function with(array $changes): static

// ArrayAccess (read-only)
public function offsetExists(mixed $offset): bool
public function offsetGet(mixed $offset): mixed

// JsonSerializable
public function jsonSerialize(): mixed
```

---

🤝 Contributing
--------------

[](#-contributing)

Contributions are welcome! Please feel free to submit a Pull Request.

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes
4. Run tests and checks (`composer check`)
5. Commit your changes (`git commit -m 'Add amazing feature'`)
6. Push to the branch (`git push origin feature/amazing-feature`)
7. Open a Pull Request

---

📄 License
---------

[](#-license)

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

---

🙏 Acknowledgments
-----------------

[](#-acknowledgments)

- Inspired by modern typed data structures in other languages
- Built with modern PHP 8.1+ features
- Tested with PHPUnit 11
- Analyzed with PHPStan Level 9

---

📧 Author
--------

[](#-author)

**Vasiliy Krivoplyas**
Email:

---

🔗 Links
-------

[](#-links)

- [GitHub Repository](https://github.com/tommyknocker/struct)
- [Packagist Package](https://packagist.org/packages/tommyknocker/struct)
- [Issue Tracker](https://github.com/tommyknocker/struct/issues)

###  Health Score

42

—

FairBetter than 90% of packages

Maintenance66

Regular maintenance activity

Popularity7

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity74

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~507 days

Recently: every ~574 days

Total

6

Last Release

202d ago

Major Versions

0.0.2 → v1.0.02025-10-09

### Community

Maintainers

![](https://www.gravatar.com/avatar/a0698ecfae8299d0a2e2fb1d31f6497bd51592402e50d74265c2d08651fcd11d?d=identicon)[tommyknocker](/maintainers/tommyknocker)

---

Top Contributors

[![tommyknocker](https://avatars.githubusercontent.com/u/7640707?v=4)](https://github.com/tommyknocker "tommyknocker (16 commits)")

---

Tags

data-structuredata-validationphpschemastructuretemplate-drivenvalidationPSR-11PHPStanschemavalidationstatic analysisdependency-injectionreflectionmetadataserializationdeserializationattributesdata-transfer-objectform validationdtoimmutablephp8array-accesstype-safeapi-validationtype-castingdata structuredata validationphp81readonlystructfactory-patterntyped propertiesjson-serializationstruct-helpertemplate-drivenruntime-validation

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/tommyknocker-struct/health.svg)

```
[![Health](https://phpackages.com/badges/tommyknocker-struct/health.svg)](https://phpackages.com/packages/tommyknocker-struct)
```

###  Alternatives

[php-di/php-di

The dependency injection container for humans

2.8k48.9M994](/packages/php-di-php-di)[laminas/laminas-servicemanager

Factory-Driven Dependency Injection Container

15955.1M694](/packages/laminas-laminas-servicemanager)[fab2s/dt0

Immutable DTOs with bidirectional casting. No framework required. 8x faster than the alternative.

101.6k1](/packages/fab2s-dt0)[bnf/phpstan-psr-container

PHPStan dynamic return type extension for PSR-11 ContainerInterface

133.5M53](/packages/bnf-phpstan-psr-container)[slince/di

A flexible dependency injection container

20260.4k6](/packages/slince-di)[yorcreative/laravel-argonaut-dto

Argonaut is a lightweight Data Transfer Object (DTO) package for Laravel that supports nested casting, recursive serialization, and validation out of the box. Ideal for service layers, APIs, and clean architecture workflows.

1062.8k1](/packages/yorcreative-laravel-argonaut-dto)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
