Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 18 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
root = true

[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false

[*.{yml,yaml}]
indent_size = 2

[composer.json]
indent_size = 4
14 changes: 14 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
* text=auto eol=lf

# Files to exclude from the distributed Composer package.
/.editorconfig export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/.github export-ignore
/.php-cs-fixer.dist.php export-ignore
/phpstan.neon.dist export-ignore
/phpunit.xml.dist export-ignore
/tests export-ignore
/docs export-ignore
/CONTRIBUTING.md export-ignore
/CODE_OF_CONDUCT.md export-ignore
84 changes: 84 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: CI

on:
push:
branches: [main, "feat/*", "fix/*"]
pull_request:
branches: [main]

jobs:
tests:
name: PHP ${{ matrix.php }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
php: ["7.4", "8.0", "8.1", "8.2", "8.3", "8.4"]
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: xdebug
tools: composer:v2

- name: Validate composer.json
run: composer validate --strict

- name: Get Composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT"

- name: Cache Composer packages
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-php${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }}
restore-keys: |
${{ runner.os }}-php${{ matrix.php }}-composer-

- name: Install dependencies
run: composer install --prefer-dist --no-progress --no-interaction

- name: Run test suite
run: vendor/bin/phpunit --testdox

static-analysis:
name: Static analysis (PHPStan)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.3"
tools: composer:v2

- name: Install dependencies
run: composer install --prefer-dist --no-progress --no-interaction

- name: PHPStan
run: vendor/bin/phpstan analyse --no-progress

code-style:
name: Code style (PHP-CS-Fixer)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.3"
tools: composer:v2

- name: Install dependencies
run: composer install --prefer-dist --no-progress --no-interaction

- name: PHP-CS-Fixer (dry-run)
run: vendor/bin/php-cs-fixer fix --dry-run --diff
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,9 @@
/.vs/
/.vscode/
/vendor/
/composer.lock
/composer.lock
/.phpunit.cache/
/.phpunit.result.cache
/.php-cs-fixer.cache
/build/
/coverage/
32 changes: 32 additions & 0 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

$finder = PhpCsFixer\Finder::create()
->in([__DIR__ . '/src', __DIR__ . '/tests'])
->name('*.php');

return (new PhpCsFixer\Config())
->setRiskyAllowed(true)
->setRules([
'@PSR12' => true,
'@PHP74Migration' => true,
'declare_strict_types' => true,
'array_syntax' => ['syntax' => 'short'],
'no_unused_imports' => true,
'ordered_imports' => ['sort_algorithm' => 'alpha'],
'single_quote' => true,
'trailing_comma_in_multiline' => true,
'no_trailing_whitespace' => true,
'no_whitespace_in_blank_line' => true,
'blank_line_after_opening_tag' => true,
'blank_line_before_statement' => ['statements' => ['return']],
'method_chaining_indentation' => true,
'native_function_invocation' => false,
'phpdoc_align' => ['align' => 'vertical'],
'phpdoc_separation' => true,
'phpdoc_trim' => true,
'no_superfluous_phpdoc_tags' => false,
])
->setFinder($finder)
->setCacheFile(__DIR__ . '/.php-cs-fixer.cache');
169 changes: 119 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,95 +1,164 @@
# InitPHP ParameterBag

Single and multi-dimensional parameter bag.
A small, dependency-free parameter container for PHP that handles both
flat and nested (dotted-path) data with the same API.

[![Latest Stable Version](http://poser.pugx.org/initphp/parameterbag/v)](https://packagist.org/packages/initphp/parameterbag) [![Total Downloads](http://poser.pugx.org/initphp/parameterbag/downloads)](https://packagist.org/packages/initphp/parameterbag) [![Latest Unstable Version](http://poser.pugx.org/initphp/parameterbag/v/unstable)](https://packagist.org/packages/initphp/parameterbag) [![License](http://poser.pugx.org/initphp/parameterbag/license)](https://packagist.org/packages/initphp/parameterbag) [![PHP Version Require](http://poser.pugx.org/initphp/parameterbag/require/php)](https://packagist.org/packages/initphp/parameterbag)
[![Latest Stable Version](https://poser.pugx.org/initphp/parameterbag/v)](https://packagist.org/packages/initphp/parameterbag)
[![Total Downloads](https://poser.pugx.org/initphp/parameterbag/downloads)](https://packagist.org/packages/initphp/parameterbag)
[![CI](https://github.com/InitPHP/ParameterBag/actions/workflows/ci.yml/badge.svg)](https://github.com/InitPHP/ParameterBag/actions/workflows/ci.yml)
[![License](https://poser.pugx.org/initphp/parameterbag/license)](https://packagist.org/packages/initphp/parameterbag)
[![PHP Version Require](https://poser.pugx.org/initphp/parameterbag/require/php)](https://packagist.org/packages/initphp/parameterbag)

![parameterbag](https://initphp.github.io/logos/parameterbag.png)

## Installation
---

```
composer require initphp/parameterbag
```
## Features

## Requirements
- Single API for flat and nested data; nesting is auto-detected from
the constructor payload or toggled explicitly.
- Dotted-path access (`$bag->get('database.user')`) with a
configurable separator.
- Optional, opt-in case-insensitive key handling.
- Implements PHP's standard collection contracts: `ArrayAccess`,
`Countable`, `IteratorAggregate`.
- Strict validation of constructor options (typos throw instead of
being silently ignored).
- Zero runtime dependencies; PHPStan level 8 clean.

- PHP 7.2 or later

## Usage
## Requirements

```php
require_once "vendor/autoload.php";
use \InitPHP\ParameterBag\ParameterBag;
- PHP 7.4 or later (including 8.0–8.4)

$parameter = new ParameterBag($_GET);
## Installation

// GET /?user=muhametsafak
echo $parameter->get('user', null); // "muhametsafak"
```bash
composer require initphp/parameterbag
```

### Using nested arrays
## Quick start

```php
require_once "vendor/autoload.php";
use \InitPHP\ParameterBag\ParameterBag;
use InitPHP\ParameterBag\ParameterBag;

$data = [
'database' => [
'dsn' => 'mysql:host=localhost',
'username' => 'root',
'password' => '123456'
]
];
$bag = new ParameterBag($_GET);

$parameter = new ParameterBag($data, ['isMulti' => true, 'separator' => '.']);
// GET /?user=alice
echo $bag->get('user', 'guest'); // 'alice'

$parameter->get('database.username'); // "root"
$parameter->has('database.charset'); // false
$bag->set('locale', 'en_US')->set('debug', true);
$bag->has('debug'); // true
$bag->remove('debug');
```

### Methods
### Nested data (multi mode)

#### `has()`
Pass a nested array (or set `isMulti => true` explicitly) and the bag
will treat the separator (`.` by default) as a path delimiter:

```php
public function has(string $key): bool;
$config = new ParameterBag([
'database' => [
'dsn' => 'mysql:host=localhost',
'username' => 'root',
'password' => 'secret',
],
]);

$config->get('database.username'); // 'root'
$config->has('database.charset'); // false
$config->set('database.charset', 'utf8mb4');
$config->remove('database.password');
```

#### `get()`
Use a custom separator if dots are part of your keys:

```php
public function get(string $key, mixed $default = null): mixed;
$bag = new ParameterBag($data, ['separator' => '|']);
$bag->get('database|username');
```

#### `set()`
### Native PHP idioms

```php
public function set(string $key, mixed $value): \InitPHP\ParameterBag\ParameterBagInterface;
```
$bag = new ParameterBag(['a' => 1, 'b' => 2]);

#### `remove()`
count($bag); // 2
$bag['c'] = 3; // ArrayAccess write
isset($bag['c']); // true
foreach ($bag as $key => $value) { /* ... */ }
```

```php
public function remove(string ...$keys): \InitPHP\ParameterBag\ParameterBagInterface;
## Public API

| Method | Purpose | Docs |
| --- | --- | --- |
| `get(string $key, mixed $default = null): mixed` | Look up a value (dotted paths in multi mode). | [usage/basic-usage](docs/usage/basic-usage.md) |
| `has(string $key): bool` | Existence check (null values count as present). | [usage/basic-usage](docs/usage/basic-usage.md) |
| `set(string $key, mixed $value): self` | Assign or replace a value. | [usage/basic-usage](docs/usage/basic-usage.md) |
| `remove(string ...$keys): self` | Delete one or more keys. | [usage/basic-usage](docs/usage/basic-usage.md) |
| `merge(array\|ParameterBagInterface ...$payloads): self` | Shallow merge (flat) or recursive replace (multi). | [usage/merging](docs/usage/merging.md) |
| `replace(array $data): self` | Swap the entire stack. | [api-reference](docs/api-reference.md) |
| `all(): array` | Return the current stack as a plain array. | [api-reference](docs/api-reference.md) |
| `keys(): array` / `values(): array` | Top-level keys / values in insertion order. | [api-reference](docs/api-reference.md) |
| `count(): int` | Top-level entry count (also via `count($bag)`). | [usage/iteration-and-counting](docs/usage/iteration-and-counting.md) |
| `getIterator(): ArrayIterator` | Iterates top-level entries. | [usage/iteration-and-counting](docs/usage/iteration-and-counting.md) |
| `isEmpty(): bool` | True when the stack has no entries. | [api-reference](docs/api-reference.md) |
| `clear(): void` | Empty the stack, keep options. | [api-reference](docs/api-reference.md) |
| `close(): void` | Empty the stack and reset options to defaults. | [api-reference](docs/api-reference.md) |

## Configuration options

The constructor accepts a second array of options. Unknown keys raise
`ParameterBagInvalidArgumentException`.

| Key | Type | Default | Description |
| --- | --- | --- | --- |
| `isMulti` | `bool` | auto-detected from `$data` | Enables dotted-path semantics. |
| `separator` | `non-empty-string` | `'.'` | Delimiter for dotted paths. Ignored in flat mode. |
| `caseInsensitive` | `bool` | `false` | When true, every key (constructor payload, set/get/has/remove arguments, merge input) is folded to lower-case. Matches the legacy v1 behaviour. |

See [docs/configuration.md](docs/configuration.md) and
[docs/usage/case-sensitivity.md](docs/usage/case-sensitivity.md).

## Exceptions

| Exception | Raised when |
| --- | --- |
| `InitPHP\ParameterBag\Exception\ParameterBagInvalidArgumentException` | Unknown option key, non-array/non-ParameterBag argument to `merge()`, or `$bag[] = $v` ArrayAccess append. Extends `\InvalidArgumentException`. |

See [docs/exceptions.md](docs/exceptions.md).

## Development

```bash
composer install
composer test # PHPUnit
composer analyse # PHPStan (level 8)
composer cs:check # PHP-CS-Fixer dry-run
composer cs:fix # PHP-CS-Fixer apply
```

#### `all()`
CI runs the matrix across PHP 7.4, 8.0, 8.1, 8.2, 8.3, and 8.4.

```php
public function all(): array;
```
## Upgrading from v1

#### `merge()`
v2 introduces a small set of intentional behaviour changes (cache
removed, `isMulti` auto-detect inverted, value-trim bug fixed,
case-sensitive by default, strict option validation, new methods).
A full migration guide lives at
[docs/upgrading-from-v1.md](docs/upgrading-from-v1.md).

```php
public function merge(array|\InitPHP\ParameterBag\ParameterBagInterface ...$merge): \InitPHP\ParameterBag\ParameterBagInterface;
```
## Contributing & Security

- [Contributing guidelines](https://github.com/InitPHP/.github/blob/main/CONTRIBUTING.md)
- [Code of Conduct](https://github.com/InitPHP/.github/blob/main/CODE_OF_CONDUCT.md)
- [Security policy](https://github.com/InitPHP/.github/blob/main/SECURITY.md)

## Credits

- [Muhammet ŞAFAK](https://www.muhammetsafak.com.tr) <<info@muhammetsafak.com.tr>>
- [Muhammet ŞAFAK](https://www.muhammetsafak.com.tr) <<info@muhammetsafak.com.tr>>

## License

Copyright &copy; 2022 - [MIT License](./LICENSE)
Released under the [MIT License](./LICENSE).
Loading
Loading