-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create basic array/object functionality
- Loading branch information
1 parent
cb66aca
commit ea4971c
Showing
17 changed files
with
3,599 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
/.gitattributes export-ignore | ||
/.github/ export-ignore | ||
/.gitignore export-ignore | ||
/composer.lock export-ignore | ||
/docs/ export-ignore | ||
/phpcs.xml export-ignore | ||
/phpunit.xml export-ignore | ||
/tests/ export-ignore |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
name: CI | ||
|
||
on: | ||
pull_request: ~ | ||
push: | ||
branches: [ main ] | ||
|
||
jobs: | ||
check-style: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v4 | ||
|
||
- name: Cache dependencies | ||
uses: actions/cache@v3 | ||
with: | ||
path: vendor | ||
key: ${{ runner.os }}-vendor-${{ hashFiles('**/composer.lock') }} | ||
|
||
- name: Setup PHP | ||
uses: shivammathur/setup-php@v2 | ||
with: | ||
php-version: '8.2' | ||
|
||
- name: Install dependencies | ||
run: composer install | ||
|
||
- name: Check code style | ||
run: vendor/bin/phpcs | ||
|
||
test-code: | ||
runs-on: ubuntu-latest | ||
strategy: | ||
fail-fast: false | ||
matrix: | ||
php: | ||
- '8.2' | ||
- '8.3' | ||
minimum_versions: [false] | ||
coverage: ['none'] | ||
include: | ||
- description: 'Minimum version' | ||
php: '8.2' | ||
minimum_versions: true | ||
- description: 'Code coverage' | ||
php: '8.2' | ||
coverage: 'pcov' | ||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v4 | ||
|
||
- name: Cache dependencies | ||
uses: actions/cache@v3 | ||
with: | ||
path: vendor | ||
key: ${{ runner.os }}-vendor-${{ hashFiles('**/composer.lock') }} | ||
|
||
- name: Setup PHP | ||
uses: shivammathur/setup-php@v2 | ||
with: | ||
php-version: ${{ matrix.php }} | ||
coverage: ${{ matrix.coverage }} | ||
|
||
- name: Install dependencies | ||
run: composer install | ||
if: matrix.minimum_versions == false | ||
|
||
- name: Install dependencies (minimum versions) | ||
run: composer update --no-interaction --prefer-lowest | ||
if: matrix.minimum_versions == true | ||
|
||
- name: Run tests | ||
run: vendor/bin/phpunit --no-coverage | ||
if: matrix.coverage == 'none' | ||
|
||
- name: Run tests with code coverage | ||
run: vendor/bin/phpunit | ||
if: matrix.coverage == 'pcov' | ||
|
||
- name: Upload code coverage report | ||
uses: codecov/codecov-action@v4-beta | ||
if: matrix.coverage == 'pcov' | ||
env: | ||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} | ||
with: | ||
file: build/coverage.xml | ||
fail_ci_if_error: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
.phpcs-cache | ||
.phpunit.cache | ||
composer.lock | ||
composer.phar | ||
/build/ | ||
/vendor/ | ||
|
||
# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control | ||
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file | ||
# composer.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Changelog | ||
|
||
All notable changes to this project will be documented in this file. | ||
|
||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | ||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | ||
|
||
## [1.0.0] 2023-11-03 | ||
|
||
### Added | ||
|
||
- Data interface | ||
- KeyedData, a basic implementation of Data | ||
- DataProxy, an abstract proxy of Data for extension |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,124 @@ | ||
# data | ||
A collection of tools for working with unstructured data, such as JSON | ||
# Focus: Data | ||
|
||
[![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%208.2-8892BF.svg?style=flat)](https://php.net/) | ||
[![Latest Stable Version](http://img.shields.io/packagist/v/focus/data.svg?style=flat)](https://packagist.org/packages/focus/data) | ||
[![CI Status](https://github.com/focusphp/data/actions/workflows/ci.yml/badge.svg?branch=main&event=push)](https://github.com/focusphp/data/actions) | ||
[![Code Coverage](https://codecov.io/gh/focusphp/data/graph/badge.svg?token=XFMRWA70FN)](https://codecov.io/gh/focusphp/data) | ||
[![License](https://img.shields.io/packagist/l/focus/data.svg?style=flat)](https://packagist.org/packages/focus/data) | ||
|
||
A collection of tools for working with unstructured data, such as JSON. | ||
|
||
## Installation | ||
|
||
The best way to install and use this package is with [composer](https://getcomposer.org/): | ||
|
||
```shell | ||
composer require focus/data | ||
``` | ||
|
||
## Usage | ||
|
||
The most basic usage is `KeyedData`, which wraps arrays and objects: | ||
|
||
```php | ||
use Focus\Data\KeyedData; | ||
|
||
$value = [ | ||
'user' => [ | ||
'name' => 'Susan Smith', | ||
'email' => 'susan@example.com', | ||
'hobbies' => [ | ||
'football', | ||
'swimming', | ||
'reading', | ||
], | ||
'deactivated_at' => null, | ||
], | ||
]; | ||
|
||
$data = KeyedData::from($value); | ||
``` | ||
|
||
Once you have an instance of data, you can access the values by using dot paths: | ||
|
||
```php | ||
$name = $data->get(path: 'user.name'); // Susan Smith | ||
$email = $data->get(path: 'user.email'); // susan@example.com | ||
``` | ||
|
||
Values that do not exist will be returned as `null`: | ||
|
||
```php | ||
$phone = $data->get(path: 'user.phone'); // null | ||
``` | ||
|
||
[JMESPath](https://jmespath.org) expressions are also supported using the search() method: | ||
|
||
```php | ||
$sports = $data->search(path: "user.hobbies[? contains(@, 'ball')]"); // ['football'] | ||
``` | ||
|
||
It is also possible to check for the existence of a path, even when the value is `null`: | ||
|
||
```php | ||
$deactivated = $data->has(path: 'user.deactivated_at'); // true | ||
``` | ||
|
||
## FAQ | ||
|
||
These are some of the most common questions about usage and design of this package. | ||
|
||
### Why does has() return true for null values? | ||
|
||
This allows detecting when input has a value that should not be overwritten. For instance, | ||
if an application sets a `deactivated_at` timestamp to indicate that the user has left, | ||
it might also need to be able to reactivate the user by setting `deactivated_at: null`: | ||
|
||
```php | ||
if ($data->get(path: 'user.deactivated_at')) { | ||
$this->userRepository->deactivate( | ||
id: $data->get(path: 'user.id'), | ||
timestamp: $data->get(path: 'user.deactivated_at'), | ||
); | ||
} elseif ($data->has(path: 'user.deactivated_at')) { | ||
$this->userRepository->activate( | ||
id: $data->get(path: 'user.id'), | ||
); | ||
} | ||
``` | ||
|
||
If has() did not return true for null values, detecting the existence of a null value would | ||
be impossible, since get() returns null for undefined paths. | ||
|
||
### Why is there a Data interface? | ||
|
||
Keen observers will note that `KeyedData` implements a `Data` interface and the existence of | ||
the `DataProxy` abstract class. This allows for customization of the implementation, despite | ||
`KeyedData` being a `final readonly` class, by using a [proxy object][proxy] to satisfy the | ||
[Open/Closed Principle][open-closed]. | ||
|
||
By default, the `DataProxy` object will forward all calls directly to the source `Data` object. | ||
This allows customizing the behavior of any method without having to implement the full `Data` | ||
interface. For example, this would modify the get() method to treat `false` values as `null`: | ||
|
||
```php | ||
use Focus\Data\Data; | ||
use Focus\Data\DataProxy; | ||
|
||
final class MyData extends DataProxy | ||
{ | ||
public function get(string $path): mixed | ||
{ | ||
$value = $this->source()->get($path); | ||
|
||
if ($value === false) { | ||
return null; | ||
} | ||
|
||
return $value; | ||
} | ||
} | ||
``` | ||
|
||
[proxy]: https://refactoring.guru/design-patterns/proxy | ||
[open-closed]: https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
{ | ||
"name": "focus/data", | ||
"description": "A collection of tools for working with unstructured data, such as JSON.", | ||
"license": "MIT", | ||
"type": "library", | ||
"authors": [ | ||
{ | ||
"name": "Woody Gilk", | ||
"email": "woody@shadowhand.com" | ||
} | ||
], | ||
"require": { | ||
"php": "^8.2", | ||
"mtdowling/jmespath.php": "^2.7" | ||
}, | ||
"require-dev": { | ||
"doctrine/coding-standard": "^12.0", | ||
"ergebnis/composer-normalize": "^2.39", | ||
"phpunit/phpunit": "^10.4" | ||
}, | ||
"minimum-stability": "stable", | ||
"autoload": { | ||
"psr-4": { | ||
"Focus\\Data\\": "src/" | ||
} | ||
}, | ||
"autoload-dev": { | ||
"psr-4": { | ||
"Focus\\Data\\Tests\\": "tests/" | ||
} | ||
}, | ||
"config": { | ||
"allow-plugins": { | ||
"dealerdirect/phpcodesniffer-composer-installer": true, | ||
"ergebnis/composer-normalize": true | ||
}, | ||
"sort-packages": true | ||
}, | ||
"scripts": { | ||
"check": "phpcs", | ||
"test": "phpunit" | ||
}, | ||
"tags": [ | ||
"json", | ||
"data", | ||
"dot", | ||
"notation", | ||
"path", | ||
"array", | ||
"object" | ||
] | ||
} |
Oops, something went wrong.