PHPackages                             rhinox/input-data - 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. [Validation &amp; Sanitization](/categories/validation)
4. /
5. rhinox/input-data

ActiveLibrary[Validation &amp; Sanitization](/categories/validation)

rhinox/input-data
=================

PHP library for parsing user input data with safe type casting, sensible defaults, and robust error handling

v0.9.12(8mo ago)03.6k↓38.9%1MITPHPCI passing

Since Aug 29Pushed 8mo ago1 watchersCompare

[ Source](https://github.com/rhinox-php/input-data)[ Packagist](https://packagist.org/packages/rhinox/input-data)[ RSS](/packages/rhinox-input-data/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (4)Versions (14)Used By (1)

InputData
=========

[](#inputdata)

[![PHP Version](https://camo.githubusercontent.com/9c2f8ad80d34105266a94c4c06234f8ed18c968d3595039c2d9a7becd1e71c8b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253345253344382e342d626c75652e737667)](https://php.net/)[![License](https://camo.githubusercontent.com/8bb50fd2278f18fc326bf71f6e88ca8f884f72f179d3e555e20ed30157190d0d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e2e737667)](LICENSE)[![Tests](https://camo.githubusercontent.com/283e17f64efdee1ecc8859bdf0367f5b7316250f059c91071ee558a3491312e8/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f74657374732d313030253235253230636f7665726167652d627269676874677265656e2e737667)](phpunit.xml)

InputData is a PHP library that helps parse user input data, or data from sources that could potentially be invalid, changed, or corrupted. It parses data based on expectations with sensible defaults and robust type casting. It does not replace validation or sanitization, but provides best-effort parsing with predictable behavior.

Features
--------

[](#features)

- **Safe type casting** with fallback defaults
- **Dot notation** for nested data access
- **Inbuilt data types**: strings, integers, decimals, booleans, dates, arrays, JSON
- **Easily extendable** for custom data types
- **Mutable and Immutable** variants for different use cases
- **ArrayAccess, Countable, Iterable** interfaces for seamless integration

Installation
------------

[](#installation)

```
composer require rhinox/input-data
```

Quick Start
-----------

[](#quick-start)

```
use Rhino\InputData\InputData;

$data = new InputData([
    'name' => 'John Doe',
    'age' => '30',
    'active' => 'true',
    'profile' => [
        'email' => 'john@example.com'
    ]
]);

echo $data->string('name');           // "John Doe"
echo $data->int('age');              // 30
echo $data->bool('active');          // true
echo $data->string('profile.email'); // "john@example.com" (dot notation)
echo $data->string('missing', 'N/A'); // "N/A" (default value)
```

Use Cases
---------

[](#use-cases)

### User Input Processing

[](#user-input-processing)

```
// HTTP request data
$post = new InputData($_POST);
$get = new InputData($_GET);

// JSON API requests
$body = InputData::tryJsonDecode(file_get_contents('php://input'));
```

### API Response Handling

[](#api-response-handling)

```
$response = (new \GuzzleHttp\Client())->get('https://api.example.com/users/1');
$data = InputData::tryJsonDecode($response->getBody());

$userId = $data->int('id');
$userName = $data->string('name', 'Unknown User');
$createdAt = $data->dateTime('created_at', 'UTC');
```

### File Processing

[](#file-processing)

```
// JSON files
$config = InputData::tryJsonDecodeFile('config.json');
$apiKey = $config->string('api_key');
```

```
// CSV processing
function readCsv(string $file) {
    $handle = fopen($file, 'r');
    for ($i = 0; $row = fgetcsv($handle); ++$i) {
        yield $i => new InputData($row);
    }
    fclose($handle);
}

foreach (readCsv('data.csv') as $i => $row) {
    if ($i === 0) continue; // Skip header
    $name = $row->string(0);
    $amount = $row->decimal(1);
    $date = $row->dateTime(2, 'UTC');
}
```

API Reference
-------------

[](#api-reference)

All methods accept a key parameter and optional default value. Keys support dot notation for nested data access.

### Type Casting Methods

[](#type-casting-methods)

#### string()

[](#string)

```
$data->string(?string $key = null, ?string $default = ''): ?string
```

Cast value to string. Non-string values are converted using PHP's string casting rules.

```
$data = new InputData(['name' => 'John', 'age' => 30, 'tags' => ['dev', 'php']]);

$data->string('name');           // "John"
$data->string('age');            // "30"
$data->string('missing');        // ""
$data->string('missing', 'N/A'); // "N/A"
$data->string('tags');           // "" (arrays can't be cast to string)
```

#### int()

[](#int)

```
$data->int(?string $key = null, ?int $default = 0): ?int
```

Cast value to integer. Non-numeric values return the default.

```
$data = new InputData(['count' => '42', 'price' => '19.99', 'name' => 'John']);

$data->int('count');           // 42
$data->int('price');           // 19
$data->int('name');            // 0 (default)
$data->int('missing', 100);    // 100
```

#### decimal()

[](#decimal)

```
$data->decimal(?string $key = null, ?float $default = 0.0): ?float
```

Cast value to float/decimal.

```
$data = new InputData(['price' => '19.99', 'count' => '5', 'name' => 'John']);

$data->decimal('price');        // 19.99
$data->decimal('count');        // 5.0
$data->decimal('name');         // 0.0 (default)
$data->decimal('missing', 1.5); // 1.5
```

#### bool()

[](#bool)

```
$data->bool(?string $key = null, ?bool $default = false): ?bool
```

Cast value to boolean.

```
$data = new InputData(['active' => 'true', 'count' => '0', 'name' => 'John']);

$data->bool('active');          // true
$data->bool('count');           // false
$data->bool('name');            // true (non-empty string)
$data->bool('missing');         // false (default)
```

#### dateTime()

[](#datetime)

```
$data->dateTime(?string $key, ?string $timezone = null, ?string $default = null): ?\DateTimeImmutable
```

Parse value as DateTime with optional timezone.

```
$data = new InputData(['created' => '2023-07-15 10:30:00', 'timestamp' => '@1689422400']);

$data->dateTime('created');                    // DateTimeImmutable (server timezone)
$data->dateTime('created', 'UTC');             // DateTimeImmutable (UTC)
$data->dateTime('timestamp');                  // DateTimeImmutable from timestamp
$data->dateTime('missing', null, 'now');       // Current time
$data->dateTime('invalid', null, null);        // null
```

### Data Structure Methods

[](#data-structure-methods)

#### arr()

[](#arr)

```
$data->arr(?string $key = null, ?array $default = []): InputData
```

Get array data as new InputData instance.

```
$data = new InputData(['users' => [['name' => 'John'], ['name' => 'Jane']]]);

$users = $data->arr('users');
foreach ($users as $user) {
    echo $user->string('name'); // Access nested data
}

$data->arr('users')->count();     // 2
$data->arr('missing')->isEmpty(); // true
```

#### object()

[](#object)

```
$data->object(?string $key = null, $default = null): InputData
```

Get object data, converting arrays to objects when needed.

```
$data = new InputData(['config' => ['debug' => true, 'timeout' => 30]]);

$config = $data->object('config');
$debug = $config->bool('debug'); // true
```

#### json()

[](#json)

```
$data->json(?string $key = null, $default = []): InputData
```

Parse JSON string into InputData instance.

```
$data = new InputData(['response' => '{"status":"ok","data":[1,2,3]}']);

$response = $data->json('response');
$status = $response->string('status');      // "ok"
$items = $response->arr('data')->count();   // 3
```

#### raw()

[](#raw)

```
$data->raw(string $key, $default = null): mixed
```

Get raw value without type casting.

```
$data = new InputData(['items' => ['a', 'b', 'c'], 'count' => '5']);

$data->raw('items');  // ['a', 'b', 'c'] (original array)
$data->raw('count');  // '5' (original string)
```

### Utility Methods

[](#utility-methods)

#### exists()

[](#exists)

```
$data->exists(string $key): bool
```

Check if a key exists (supports dot notation).

```
$data = new InputData(['user' => ['profile' => ['name' => 'John']]]);

$data->exists('user');              // true
$data->exists('user.profile.name'); // true
$data->exists('user.missing');      // false
```

#### isEmpty(), isArray(), count()

[](#isempty-isarray-count)

```
$data->isEmpty(): bool
$data->isArray(): bool
$data->count(): int
```

Check data state and get count.

```
$data = new InputData(['a', 'b', 'c']);
$data->isEmpty(); // false
$data->isArray(); // true
$data->count();   // 3
```

Static Factory Methods
----------------------

[](#static-factory-methods)

### JSON Processing

[](#json-processing)

#### jsonDecode()

[](#jsondecode)

```
InputData::jsonDecode(string $jsonString, bool $assoc = true): InputData
```

Parse JSON string, throws `JsonException` on invalid JSON.

```
try {
    $data = InputData::jsonDecode('{"name":"John","age":30}');
    echo $data->string('name'); // "John"
} catch (\JsonException $e) {
    // Handle parsing error
}
```

#### tryJsonDecode()

[](#tryjsondecode)

```
InputData::tryJsonDecode(string $jsonString, bool $assoc = true): InputData
```

Parse JSON string, returns empty InputData on invalid JSON.

```
$data = InputData::tryJsonDecode('invalid json');
$data->isEmpty(); // true - no exception thrown
```

#### jsonDecodeFile()

[](#jsondecodefile)

```
InputData::jsonDecodeFile(string $filename, bool $assoc = true): InputData
```

Read and parse JSON file, throws `FileReadException` or `JsonException` on errors.

```
$config = InputData::tryJsonDecodeFile('config.json');
$apiKey = $config->string('api_key');
```

#### tryJsonDecodeFile()

[](#tryjsondecodefile)

```
InputData::tryJsonDecodeFile(string $filename, bool $assoc = true): InputData
```

Read and parse JSON file, returns empty InputData on any error.

```
$config = InputData::tryJsonDecodeFile('config.json');
if (!$config->isEmpty()) {
    $apiKey = $config->string('api_key');
}
```

Data Modification
-----------------

[](#data-modification)

Two variants are available for modifying data:

- **MutableInputData**: Methods modify the current instance
- **ImmutableInputData**: Methods return new instances

### MutableInputData

[](#mutableinputdata)

```
use Rhino\InputData\MutableInputData;

$data = new MutableInputData(['a' => 1, 'b' => 2]);

$data->set('c', 3);              // Modifies $data
$data->extend(['d' => 4]);       // Modifies $data
$data->filter(fn($v) => $v->int() > 2); // Modifies $data

echo $data->count(); // 2 (only c=3, d=4 remain)
```

### ImmutableInputData

[](#immutableinputdata)

```
use Rhino\InputData\ImmutableInputData;

$original = new ImmutableInputData(['a' => 1, 'b' => 2]);

$modified = $original->set('c', 3);              // Returns new instance
$extended = $original->extend(['d' => 4]);       // Returns new instance
$filtered = $original->filter(fn($v) => $v->int() > 1); // Returns new instance

echo $original->count(); // 2 (unchanged)
echo $modified->count(); // 3 (new instance)
```

### Modification Methods

[](#modification-methods)

#### extend()

[](#extend)

```
$data->extend(array ...$newData): static
```

Recursively merge arrays into existing data.

```
$data = new MutableInputData(['user' => ['name' => 'John']]);
$data->extend(['user' => ['email' => 'john@example.com'], 'active' => true]);
// Result: ['user' => ['name' => 'John', 'email' => 'john@example.com'], 'active' => true]
```

#### set()

[](#set)

```
$data->set(string $name, $value): static
```

Set value at path (supports dot notation).

```
$data = new MutableInputData([]);
$data->set('user.profile.name', 'John');
$data->set('user.profile.age', 30);
// Result: ['user' => ['profile' => ['name' => 'John', 'age' => 30]]]
```

#### unset()

[](#unset)

```
$data->unset(string $name): static
```

Remove value at path (supports dot notation).

```
$data = new MutableInputData(['user' => ['name' => 'John', 'email' => 'john@example.com']]);
$data->unset('user.email');
// Result: ['user' => ['name' => 'John']]
```

#### filter()

[](#filter)

```
$data->filter(?callable $callback = null): static
```

Filter data using callback. Default callback removes null values.

```
$data = new MutableInputData(['a' => 1, 'b' => null, 'c' => 3]);
$data->filter(); // Removes null values
// Result: ['a' => 1, 'c' => 3]

$data->filter(fn($value) => $value->int() > 1);
// Result: ['c' => 3]
```

#### filterRecursive()

[](#filterrecursive)

```
$data->filterRecursive(?callable $callback = null): static
```

Recursively filter nested data.

```
$data = new MutableInputData([
    'users' => [
        ['name' => 'John', 'active' => true],
        ['name' => 'Jane', 'active' => false]
    ]
]);
$data->filterRecursive(fn($value, $key) => $key->string() !== 'active' || $value->bool());
```

#### map()

[](#map)

```
$data->map(callable $callback): static
```

Transform values using callback.

```
$data = new MutableInputData(['a' => 1, 'b' => 2, 'c' => 3]);
$data->map(fn($value) => $value->int() * 2);
// Result: ['a' => 2, 'b' => 4, 'c' => 6]
```

#### mapRecursive()

[](#maprecursive)

```
$data->mapRecursive(callable $callback): static
```

Recursively transform nested data.

```
$data = new MutableInputData(['numbers' => [1, 2], 'single' => 3]);
$data->mapRecursive(fn($value) => $value->int() * 2);
// Result: ['numbers' => [2, 4], 'single' => 6]
```

#### values()

[](#values)

```
$data->values(): static
```

Re-index array to sequential numeric keys.

```
$data = new MutableInputData(['a' => 1, 'c' => 2, 'x' => 3]);
$data->values();
// Result: [0 => 1, 1 => 2, 2 => 3]
```

#### merge()

[](#merge)

```
$data->merge($data): static
```

Merge with another array or InputData instance.

```
$data1 = new MutableInputData(['a' => 1, 'b' => 2]);
$data1->merge(['c' => 3, 'd' => 4]);
// Result: ['a' => 1, 'b' => 2, 'c' => 3, 'd' => 4]
```

Advanced Features
-----------------

[](#advanced-features)

### Dot Notation

[](#dot-notation)

Access nested data using dot-separated keys:

```
$data = new InputData([
    'user' => [
        'profile' => [
            'social' => [
                'twitter' => '@johndoe'
            ]
        ]
    ]
]);

$twitter = $data->string('user.profile.social.twitter'); // "@johndoe"
```

### Array Access Interface

[](#array-access-interface)

InputData implements ArrayAccess for convenient access:

```
$data = new InputData(['name' => 'John', 'age' => 30]);

echo $data['name']->string(); // "John"
echo $data['age']->int();     // 30
isset($data['name']);         // true

// Note: Setting/unsetting throws MutationException for base InputData
```

### Iteration

[](#iteration)

InputData implements IteratorAggregate:

```
$data = new InputData(['a' => 1, 'b' => 2, 'c' => 3]);

foreach ($data as $key => $value) {
    echo $key->string() . ': ' . $value->int();
}
```

### Find Method

[](#find-method)

Search for items matching criteria:

```
$users = new InputData([
    ['name' => 'John', 'active' => true],
    ['name' => 'Jane', 'active' => false],
    ['name' => 'Bob', 'active' => true]
]);

$activeUser = $users->find(fn($user) => $user->bool('active'));
echo $activeUser->string('name'); // "John"
```

Extending InputData
-------------------

[](#extending-inputdata)

Create custom parsers by extending the base class:

```
class CustomInputData extends InputData
{
    public function uuid(?string $name = null, ?string $default = null): ?string
    {
        $value = $this->string($name, $default);

        if ($value && preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', $value)) {
            return $value;
        }

        return $default;
    }

    public function email(?string $name = null, ?string $default = null): ?string
    {
        $value = $this->string($name, $default);

        if ($value && filter_var($value, FILTER_VALIDATE_EMAIL)) {
            return $value;
        }

        return $default;
    }
}

$data = new CustomInputData(['id' => 'f47ac10b-58cc-4372-a567-0e02b2c3d479']);
$uuid = $data->uuid('id'); // Returns the UUID or null if invalid
```

Development
-----------

[](#development)

### Requirements

[](#requirements)

- PHP 8.4 or higher
- Composer for dependency management

### Development Tools

[](#development-tools)

The project uses modern PHP development tools:

- **PHPUnit**: Testing framework with 100% code coverage
- **PHP CS Fixer**: Code formatting and style checking
- **Psalm**: Static analysis for type safety
- **Infection**: Mutation testing

### Running Tests

[](#running-tests)

```
# Run all tests
composer test:unit

# Run tests with coverage report
composer test:coverage

# Run tests with text coverage
composer test:coverage-text
```

### Code Quality

[](#code-quality)

```
# Check code style
composer lint:check

# Fix code style issues
composer lint:fix

# Run static analysis
composer analyze

# Run full quality assurance suite
composer qa
```

Contributing
------------

[](#contributing)

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes with tests
4. Run the quality assurance suite (`composer qa`)
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

Please ensure all tests pass and maintain 100% code coverage.

License
-------

[](#license)

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

###  Health Score

39

—

LowBetter than 86% of packages

Maintenance60

Regular maintenance activity

Popularity22

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity52

Maturing project, gaining track record

 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 ~152 days

Recently: every ~345 days

Total

13

Last Release

254d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/134799?v=4)[David Neilsen](/maintainers/Petah)[@Petah](https://github.com/Petah)

---

Top Contributors

[![Petah](https://avatars.githubusercontent.com/u/134799?v=4)](https://github.com/Petah "Petah (43 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPsalm

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/rhinox-input-data/health.svg)

```
[![Health](https://phpackages.com/badges/rhinox-input-data/health.svg)](https://phpackages.com/packages/rhinox-input-data)
```

###  Alternatives

[webmozart/assert

Assertions to validate method input/output with nice error messages.

7.6k894.0M1.2k](/packages/webmozart-assert)[bensampo/laravel-enum

Simple, extensible and powerful enumeration implementation for Laravel.

2.0k15.9M104](/packages/bensampo-laravel-enum)[swaggest/json-schema

High definition PHP structures with JSON-schema based validation

48612.5M73](/packages/swaggest-json-schema)[stevebauman/purify

An HTML Purifier / Sanitizer for Laravel

5325.6M19](/packages/stevebauman-purify)[ashallendesign/laravel-config-validator

A package for validating your Laravel app's config.

217905.3k5](/packages/ashallendesign-laravel-config-validator)[crazybooot/base64-validation

Laravel validators for base64 encoded files

1341.9M8](/packages/crazybooot-base64-validation)

PHPackages © 2026

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