.State 0000666 00000000012 13371317540 0005626 0 ustar 00 finalized
.gitignore 0000666 00000000030 13371317540 0006535 0 ustar 00 /vendor/
/composer.lock
.travis.yml 0000666 00000003312 13371317540 0006664 0 ustar 00 language: php
matrix:
include:
- php: 5.5
- php: 5.6
- php: 7.0
- php: 7.1
env:
- ENABLE_XDEBUG=true
- php: 7.1
env:
- ENABLE_DEVTOOLS=true
- php: nightly
- php: hhvm-3.12
sudo: required
dist: trusty
group: edge
- php: hhvm
sudo: required
dist: trusty
group: edge
allow_failures:
- php: nightly
- php: hhvm-3.12
- php: hhvm
fast_finish: true
os:
- linux
notifications:
irc: "chat.freenode.net#hoaproject"
sudo: false
env:
global:
- secure: "AAAAB3NzaC1yc2EAAAADAQABAAACAQDIf0Rf76Hhkflz5b9UzWjOjk4UlMU5ySk0VY3B4WdHDLWMMK7fBp1Aj9qXWEDwkuX/NbQP1gB8jQNo7i5uZEOfu7Mn2svPkBBtnmKmaJhk90xypM4lcpcdPi4e8kXUgkriNQLQ2bRe1qZIeF115FkuIvActq7iWKY1TVSZbO54cDKMifDZfH09cf4vpwrZJqwZG6PUnUcCYijgDy99HtfRvzf9xalO4yWm55ZEbJ/VNTHlq1EhK73QLdHC7MO+OQFcd5wEyMbNxBj/bDn/udgb0HsrDijComTg/oTdQJMspYDQYV3ZYvpGozTTCVQrVTYYTP9RCNstgJLHDv9fZZW6yRlw4yNsT7jIQRLs/7awTxOAvRlxqaxk0//ECVNhDgawVtlbEIKrqnM1N7QTm0gjE0HkWEzxE0QbgoZqlLFD6qCp6WVvIT3uGY/i4TkVy78wf3/fzCKbrf72kYSbxIOCxVtptOmrgAblNEpiA/uZ9IofR2p2iwiVY1xF/mzxV2M4zCw6WASrlDhkaL0IncEdRtBuV2WTpixmtjmNkE9h/90kzb5cKExU786gZmvyflYvqlNlcMo3dNsDnROjQCAUXGBw5+risdqTT295BGmlEdZUtcf0c6/zEGhR8B7CktWYLSgOL5mpGMVNEBzyzEwnIiWCvI3pGgoV3Z9UzSJWKQ=="
cache:
directories:
- vendor/
before_script:
- export PATH="$PATH:$HOME/.composer/vendor/bin"
- if [[ ! $ENABLE_XDEBUG ]]; then
phpenv config-rm xdebug.ini || echo "ext-xdebug is not available, cannot remove it.";
fi
script:
- composer install
- vendor/bin/hoa test:run
- if [[ $ENABLE_DEVTOOLS ]]; then
composer global require friendsofphp/php-cs-fixer;
vendor/bin/hoa devtools:cs --diff --dry-run .;
fi
Autoloader.php 0000666 00000016722 13371317540 0007374 0 ustar 00 _namespacePrefixesToBaseDirectories[$prefix])) {
$this->_namespacePrefixesToBaseDirectories[$prefix] = [];
}
if (true === $prepend) {
array_unshift(
$this->_namespacePrefixesToBaseDirectories[$prefix],
$baseDirectory
);
} else {
array_push(
$this->_namespacePrefixesToBaseDirectories[$prefix],
$baseDirectory
);
}
return;
}
/**
* Try to load the entity file for a given entity name.
*
* @param string $entity Entity name to load.
* @return bool
*/
public function load($entity)
{
$entityPrefix = $entity;
$hasBaseDirectory = false;
while (false !== $pos = strrpos($entityPrefix, '\\')) {
$currentEntityPrefix = substr($entity, 0, $pos + 1);
$entityPrefix = rtrim($currentEntityPrefix, '\\');
$entitySuffix = substr($entity, $pos + 1);
$entitySuffixAsPath = str_replace('\\', '/', $entitySuffix);
if (false === $this->hasBaseDirectory($currentEntityPrefix)) {
continue;
}
$hasBaseDirectory = true;
foreach ($this->getBaseDirectories($currentEntityPrefix) as $baseDirectory) {
$file = $baseDirectory . $entitySuffixAsPath . '.php';
if (false !== $this->requireFile($file)) {
return $file;
}
}
}
if (true === $hasBaseDirectory &&
$entity === Consistency::getEntityShortestName($entity) &&
false !== $pos = strrpos($entity, '\\')) {
return $this->runAutoloaderStack(
$entity . '\\' . substr($entity, $pos + 1)
);
}
return null;
}
/**
* Require a file if exists.
*
* @param string $filename File name.
* @return bool
*/
public function requireFile($filename)
{
if (false === file_exists($filename)) {
return false;
}
require $filename;
return true;
}
/**
* Check whether at least one base directory exists for a namespace prefix.
*
* @param string $namespacePrefix Namespace prefix.
* @return bool
*/
public function hasBaseDirectory($namespacePrefix)
{
return isset($this->_namespacePrefixesToBaseDirectories[$namespacePrefix]);
}
/**
* Get declared base directories for a namespace prefix.
*
* @param string $namespacePrefix Namespace prefix.
* @return array
*/
public function getBaseDirectories($namespacePrefix)
{
if (false === $this->hasBaseDirectory($namespacePrefix)) {
return [];
}
return $this->_namespacePrefixesToBaseDirectories[$namespacePrefix];
}
/**
* Get loaded classes.
*
* @return array
*/
public static function getLoadedClasses()
{
return get_declared_classes();
}
/**
* Run the entire autoloader stack with a specific entity.
*
* @param string $entity Entity name to load.
* @return void
*/
public function runAutoloaderStack($entity)
{
return spl_autoload_call($entity);
}
/**
* Register the autoloader.
*
* @param bool $prepend Prepend this autoloader to the stack or not.
* @return bool
*/
public function register($prepend = false)
{
return spl_autoload_register([$this, 'load'], true, $prepend);
}
/**
* Unregister the autoloader.
*
* @return bool
*/
public function unregister()
{
return spl_autoload_unregister([$this, 'load']);
}
/**
* Get all registered autoloaders (not only from this library).
*
* @return array
*/
public function getRegisteredAutoloaders()
{
return spl_autoload_functions();
}
/**
* Dynamic new, a simple factory.
* It loads and constructs a class, with provided arguments.
*
* @param bool $classname Classname.
* @param array $arguments Arguments for the constructor.
* @return object
*/
public static function dnew($classname, array $arguments = [])
{
$classname = ltrim($classname, '\\');
if (false === Consistency::entityExists($classname, false)) {
spl_autoload_call($classname);
}
$class = new \ReflectionClass($classname);
if (empty($arguments) || false === $class->hasMethod('__construct')) {
return $class->newInstance();
}
return $class->newInstanceArgs($arguments);
}
}
/**
* Autoloader.
*/
$autoloader = new Autoloader();
$autoloader->addNamespace('Hoa', dirname(__DIR__));
$autoloader->register();
CHANGELOG.md 0000666 00000005440 13371317540 0006370 0 ustar 00 # 1.17.05.02
* CI: Set up Travis. (Ivan Enderlin, 2017-03-08T09:52:17+01:00)
* Prelude: Remove the `(unset)` cast. (Ivan Enderlin, 2017-03-07T16:55:13+01:00)
# 1.17.01.10
* Quality: Happy new year! (Alexis von Glasow, 2017-01-09T21:38:10+01:00)
* Documentation: New `README.md` file. (Ivan Enderlin, 2016-10-19T16:27:31+02:00)
* Documentation: Fix `docs` and `source` links. (Ivan Enderlin, 2016-10-05T20:26:20+02:00)
* Documentation: Update `support` properties. (Ivan Enderlin, 2016-10-05T15:56:01+02:00)
* Consistency: `void` is a reserved keyword now. (Ivan Enderlin, 2016-09-02T11:06:18+02:00)
* Consistency: Remove `trait_exists` polyfill. (Ivan Enderlin, 2016-08-23T17:26:07+02:00)
# 1.16.03.03
* Add `STREAM_CRYPTO_METHOD_*` constants on PHP 5.5. (Metalaka, 2016-02-29T21:10:14+01:00)
* Composer: Fix `hoa/stream` dependency. (Ivan Enderlin, 2016-03-03T10:13:50+01:00)
# 1.16.01.14
* Test: Write cases for flex entity in autoloader. (Ivan Enderlin, 2016-01-14T10:45:08+01:00)
* Autoloader: Restrict loads to mapped entities. (Ivan Enderlin, 2016-01-14T10:38:21+01:00)
# 1.16.01.11
* Quality: Drop PHP5.4. (Ivan Enderlin, 2016-01-11T09:15:26+01:00)
* Quality: Run devtools:cs. (Ivan Enderlin, 2016-01-09T08:58:31+01:00)
* Core: Remove `Hoa\Core`. (Ivan Enderlin, 2016-01-09T08:03:33+01:00)
# 0.16.01.06
* Prelude: Introduce the prelude/preamble! (Ivan Enderlin, 2015-12-09T08:34:16+01:00)
* Consistency: Import last methods from `Hoa\Core`. (Ivan Enderlin, 2015-12-09T08:24:02+01:00)
* Quality: Fix CS. (Ivan Enderlin, 2015-12-09T06:43:22+01:00)
* Add a `.gitignore` file. (Metalaka, 2015-12-03T13:21:11+01:00)
* Autoloader: Propagate unknown entity on the stack. (Ivan Enderlin, 2015-12-03T11:04:57+01:00)
* Autoloader: Auto-register to support flex entity. (Ivan Enderlin, 2015-12-03T10:01:23+01:00)
* Composer: Force some files to load. (Ivan Enderlin, 2015-12-03T08:15:55+01:00)
* Test: Simplify `case_register`. (Ivan Enderlin, 2015-12-03T08:15:19+01:00)
* Consistency: Use a strict equality check on trait. (Ivan Enderlin, 2015-12-02T17:11:50+01:00)
* README: First draft. (Ivan Enderlin, 2015-12-02T08:40:34+01:00)
* Documentation: Update API documentation. (Ivan Enderlin, 2015-12-02T08:37:58+01:00)
* Autoloader: Support flex entities. (Ivan Enderlin, 2015-12-02T08:18:39+01:00)
* Test: Write test suite of `…nsistency\Autoloader`. (Ivan Enderlin, 2015-12-01T08:42:30+01:00)
* Test: Write test suite of `…sistency\Consistency`. (Ivan Enderlin, 2015-11-25T22:10:30+01:00)
* Test: Write test suite of `…onsistency\Xcallable`. (Ivan Enderlin, 2015-11-25T08:45:59+01:00)
* Test: Write test suite of `…onsistency\Exception`. (Ivan Enderlin, 2015-11-24T16:59:18+01:00)
* Split from `Hoa\Core`. (Ivan Enderlin, 2015-11-23T23:08:19+01:00)
(first snapshot)
Consistency.php 0000666 00000023052 13371317540 0007570 0 ustar 00 = $count) {
return $entityName;
}
if ($parts[$count - 2] === $parts[$count - 1]) {
return implode('\\', array_slice($parts, 0, -1));
}
return $entityName;
}
/**
* Declare a flex entity (for nested library).
*
* @param string $entityName Entity name.
* @return bool
*/
public static function flexEntity($entityName)
{
return class_alias(
$entityName,
static::getEntityShortestName($entityName),
false
);
}
/**
* Whether a word is reserved or not.
*
* @param string $word Word.
* @return bool
*/
public static function isKeyword($word)
{
static $_list = [
// PHP keywords.
'__halt_compiler',
'abstract',
'and',
'array',
'as',
'bool',
'break',
'callable',
'case',
'catch',
'class',
'clone',
'const',
'continue',
'declare',
'default',
'die',
'do',
'echo',
'else',
'elseif',
'empty',
'enddeclare',
'endfor',
'endforeach',
'endif',
'endswitch',
'endwhile',
'eval',
'exit',
'extends',
'false',
'final',
'float',
'for',
'foreach',
'function',
'global',
'goto',
'if',
'implements',
'include',
'include_once',
'instanceof',
'insteadof',
'int',
'interface',
'isset',
'list',
'mixed',
'namespace',
'new',
'null',
'numeric',
'object',
'or',
'print',
'private',
'protected',
'public',
'require',
'require_once',
'resource',
'return',
'static',
'string',
'switch',
'throw',
'trait',
'true',
'try',
'unset',
'use',
'var',
'void',
'while',
'xor',
'yield',
// Compile-time constants.
'__class__',
'__dir__',
'__file__',
'__function__',
'__line__',
'__method__',
'__namespace__',
'__trait__'
];
return in_array(strtolower($word), $_list);
}
/**
* Whether an ID is a valid PHP identifier.
*
* @param string $id ID.
* @return bool
*/
public static function isIdentifier($id)
{
return 0 !== preg_match(
'#^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x80-\xff]*$#',
$id
);
}
/**
* Register a register shutdown function.
* It may be analogous to a super static destructor.
*
* @param callable $callable Callable.
* @return bool
*/
public static function registerShutdownFunction($callable)
{
return register_shutdown_function($callable);
}
/**
* Get PHP executable.
*
* @return string
*/
public static function getPHPBinary()
{
if (defined('PHP_BINARY')) {
return PHP_BINARY;
}
if (isset($_SERVER['_'])) {
return $_SERVER['_'];
}
foreach (['', '.exe'] as $extension) {
if (file_exists($_ = PHP_BINDIR . DS . 'php' . $extension)) {
return realpath($_);
}
}
return null;
}
/**
* Generate an Universal Unique Identifier (UUID).
*
* @return string
*/
public static function uuid()
{
return sprintf(
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0x0fff) | 0x4000,
mt_rand(0, 0x3fff) | 0x8000,
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0xffff)
);
}
}
}
namespace
{
if (70000 > PHP_VERSION_ID && false === interface_exists('Throwable', false)) {
/**
* Implement a fake Throwable class, introduced in PHP7.0.
*/
interface Throwable
{
public function getMessage();
public function getCode();
public function getFile();
public function getLine();
public function getTrace();
public function getPrevious();
public function getTraceAsString();
public function __toString();
}
}
/**
* Define TLSv* constants, introduced in PHP 5.5.
*/
if (50600 > PHP_VERSION_ID) {
$define = function ($constantName, $constantValue, $case = false) {
if (!defined($constantName)) {
return define($constantName, $constantValue, $case);
}
return false;
};
$define('STREAM_CRYPTO_METHOD_TLSv1_0_SERVER', 8);
$define('STREAM_CRYPTO_METHOD_TLSv1_1_SERVER', 16);
$define('STREAM_CRYPTO_METHOD_TLSv1_2_SERVER', 32);
$define('STREAM_CRYPTO_METHOD_ANY_SERVER', 62);
$define('STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT', 9);
$define('STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT', 17);
$define('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT', 33);
$define('STREAM_CRYPTO_METHOD_ANY_CLIENT', 63);
}
if (!function_exists('curry')) {
/**
* Curry.
* Example:
* $c = curry('str_replace', …, …, 'foobar');
* var_dump($c('foo', 'baz')); // bazbar
* $c = curry('str_replace', 'foo', 'baz', …);
* var_dump($c('foobarbaz')); // bazbarbaz
* Nested curries also work:
* $c1 = curry('str_replace', …, …, 'foobar');
* $c2 = curry($c1, 'foo', …);
* var_dump($c2('baz')); // bazbar
* Obviously, as the first argument is a callable, we can combine this with
* \Hoa\Consistency\Xcallable ;-).
* The “…” character is the HORIZONTAL ELLIPSIS Unicode character (Unicode:
* 2026, UTF-8: E2 80 A6).
*
* @param mixed $callable Callable (two parts).
* @param ... ... Arguments.
* @return \Closure
*/
function curry($callable)
{
$arguments = func_get_args();
array_shift($arguments);
$ii = array_keys($arguments, …, true);
return function () use ($callable, $arguments, $ii) {
return call_user_func_array(
$callable,
array_replace($arguments, array_combine($ii, func_get_args()))
);
};
}
}
/**
* Flex entity.
*/
Hoa\Consistency\Consistency::flexEntity('Hoa\Consistency\Consistency');
}
Exception.php 0000666 00000003622 13371317540 0007226 0 ustar 00
---
Hoa is a modular, extensible and
structured set of PHP libraries.
Moreover, Hoa aims at being a bridge between industrial and research worlds.
# Hoa\Consistency
[![Help on IRC](https://img.shields.io/badge/help-%23hoaproject-ff0066.svg)](https://webchat.freenode.net/?channels=#hoaproject)
[![Help on Gitter](https://img.shields.io/badge/help-gitter-ff0066.svg)](https://gitter.im/hoaproject/central)
[![Documentation](https://img.shields.io/badge/documentation-hack_book-ff0066.svg)](https://central.hoa-project.net/Documentation/Library/Consistency)
[![Board](https://img.shields.io/badge/organisation-board-ff0066.svg)](https://waffle.io/hoaproject/consistency)
This library provides a thin layer between PHP VMs and libraries to ensure
consistency accross VM versions and library versions.
[Learn more](https://central.hoa-project.net/Documentation/Library/Consistency).
## Installation
With [Composer](https://getcomposer.org/), to include this library into
your dependencies, you need to
require [`hoa/consistency`](https://packagist.org/packages/hoa/consistency):
```sh
$ composer require hoa/consistency '~1.0'
```
For more installation procedures, please read [the Source
page](https://hoa-project.net/Source.html).
## Testing
Before running the test suites, the development dependencies must be installed:
```sh
$ composer install
```
Then, to run all the test suites:
```sh
$ vendor/bin/hoa test:run
```
For more information, please read the [contributor
guide](https://hoa-project.net/Literature/Contributor/Guide.html).
## Quick usage
We propose a quick overview of how the consistency API ensures foreward and
backward compatibility, also an overview of the [PSR-4
autoloader](http://www.php-fig.org/psr/psr-4/) and the xcallable API.
### Foreward and backward compatibility
The `Hoa\Consistency\Consistency` class ensures foreward and backward
compatibility.
#### Example with keywords
The `Hoa\Consistency\Consistency::isKeyword` checks whether a specific word is
reserved by PHP or not. Let's say your current PHP version does not support the
`callable` keyword or type declarations such as `int`, `float`, `string` etc.,
the `isKeyword` method will tell you if they are reserved keywords: Not only
for your current PHP version, but maybe in an incoming version.
```php
$isKeyword = Hoa\Consistency\Consistency::isKeyword('yield');
```
It avoids to write algorithms that might break in the future or for your users
living on the edge.
#### Example with identifiers
PHP identifiers are defined by a regular expression. It might change in the
future. To prevent breaking your algorithms, you can use the
`Hoa\Consistency\Consistency::isIdentifier` method to check an identifier is
correct regarding current PHP version:
```php
$isValidIdentifier = Hoa\Consistency\Consistency::isIdentifier('foo');
```
#### Flexible entities
Flexible entities are very simple. If we declare `Foo\Bar\Bar` as a flexible
entity, we will be able to access it with the `Foo\Bar\Bar` name or `Foo\Bar`.
This is very useful if your architecture evolves but you want to keep the
backward compatibility. For instance, it often happens that you create a
`Foo\Bar\Exception` class in the `Foo/Bar/Exception.php` file. But after few
versions, you realise other exceptions need to be introduced, so you need an
`Exception` directory. In this case, `Foo\Bar\Exception` should move as
`Foo\Bar\Exception\Exception`. If this latter is declared as a flexible entity,
backward compatibility will be kept.
```php
Hoa\Consistency\Consistency::flexEntity('Foo\Bar\Exception\Exception');
```
Another example is the “entry-class” (informal naming).
`Hoa\Consistency\Consistency` is a good example. This is more convenient to
write `Hoa\Consistency` instead of `Hoa\Consistency\Consistency`. This is
possible because this is a flexible entity.
#### Throwable & co.
The `Throwable` interface has been introduced to represent a whole new exception
architecture in PHP. Thus, to be compatible with incoming PHP versions, you
might want to use this interface in some cases. Hopefully, the `Throwable`
interface will be created for you if it does not exists.
```php
try {
…
} catch (Throwable $e) {
…
}
```
### Autoloader
`Hoa\Consistency\Autoloader` is a [PSR-4
compatible](http://www.php-fig.org/psr/psr-4/) autoloader. It simply works as
follows:
* `addNamespace` is used to map a namespace prefix to a directory,
* `register` is used to register the autoloader.
The API also provides the `load` method to force the load of an entity,
`unregister` to unregister the autoloader, `getRegisteredAutoloaders` to get
a list of all registered autoloaders etc.
For instance, to map the `Foo\Bar` namespace to the `Source/` directory:
```php
$autoloader = new Hoa\Consistency\Autoloader();
$autoloader->addNamespace('Foo\Bar', 'Source');
$autoloader->register();
$baz = new Foo\Bar\Baz(); // automatically loaded!
```
### Xcallable
Xcallables are “extended callables”. It is a unified API to invoke callables of
any kinds, and also extends some Hoa's API (like
[`Hoa\Event`](https://central.hoa-project.net/Resource/Library/Event)
or
[`Hoa\Stream`](https://central.hoa-project.net/Resource/Library/Stream)). It
understands the following kinds:
* `'function'` as a string,
* `'class::method'` as a string,
* `'class', 'method'` as 2 string arguments,
* `$object, 'method'` as 2 arguments,
* `$object, ''` as 2 arguments, the “able” is unknown,
* `function (…) { … }` as a closure,
* `['class', 'method']` as an array of strings,
* `[$object, 'method']` as an array.
To use it, simply instanciate the `Hoa\Consistency\Xcallable` class and use it
as a function:
```php
$xcallable = new Hoa\Consistency\Xcallable('strtoupper');
var_dump($xcallable('foo'));
/**
* Will output:
* string(3) "FOO"
*/
```
The `Hoa\Consistency\Xcallable::distributeArguments` method invokes the callable
but the arguments are passed as an array:
```php
$xcallable->distributeArguments(['foo']);
```
This is also possible to get a unique hash of the callable:
```php
var_dump($xcallable->getHash());
/**
* Will output:
* string(19) "function#strtoupper"
*/
```
Finally, this is possible to get a reflection instance of the current callable
(can be of kind [`ReflectionFunction`](http://php.net/ReflectionFunction),
[`ReflectionClass`](http://php.net/ReflectionClass),
[`ReflectionMethod`](http://php.net/ReflectionMethod) or
[`ReflectionObject`](http://php.net/ReflectionObject)):
```php
var_dump($xcallable->getReflection());
/**
* Will output:
* object(ReflectionFunction)#42 (1) {
* ["name"]=>
* string(10) "strtoupper"
* }
*/
```
When the object is set but not the method, the latter will be deduced if
possible. If the object is of kind
[`Hoa\Stream`](http://central.hoa-project.net/Resource/Library/Stream), then
according to the type of the arguments given to the callable, the
`writeInteger`, `writeString`, `writeArray` etc. method will be used. If the
argument is of kind `Hoa\Event\Bucket`, then the method name will be deduced
based on the data contained inside the event bucket. This is very handy. For
instance, the following example will work seamlessly:
```php
Hoa\Event\Event::getEvent('hoa://Event/Exception')
->attach(new Hoa\File\Write('Exceptions.log'));
```
The `attach` method on `Hoa\Event\Event` transforms its argument as an
xcallable. In this particular case, the method to call is unknown, we only have
an object (of kind `Hoa\File\Write`). However, because this is a stream, the
method will be deduced according to the data contained in the event bucket fired
on the `hoa://Event/Exception` event channel.
## Documentation
The
[hack book of `Hoa\Consistency`](https://central.hoa-project.net/Documentation/Library/Consistency)
contains detailed information about how to use this library and how it works.
To generate the documentation locally, execute the following commands:
```sh
$ composer require --dev hoa/devtools
$ vendor/bin/hoa devtools:documentation --open
```
More documentation can be found on the project's website:
[hoa-project.net](https://hoa-project.net/).
## Getting help
There are mainly two ways to get help:
* On the [`#hoaproject`](https://webchat.freenode.net/?channels=#hoaproject)
IRC channel,
* On the forum at [users.hoa-project.net](https://users.hoa-project.net).
## Contribution
Do you want to contribute? Thanks! A detailed [contributor
guide](https://hoa-project.net/Literature/Contributor/Guide.html) explains
everything you need to know.
## License
Hoa is under the New BSD License (BSD-3-Clause). Please, see
[`LICENSE`](https://hoa-project.net/LICENSE) for details.
Test/Unit/Autoloader.php 0000666 00000026307 13371317540 0011232 0 ustar 00 given(
$autoloader = new SUT(),
$prefix = 'Foo\Bar\\',
$baseDirectoryA = 'Source/Foo/Bar/',
$baseDirectoryB = 'Source/Foo/Bar/'
)
->when(
$autoloader->addNamespace($prefix, $baseDirectoryA),
$result = $autoloader->addNamespace($prefix, $baseDirectoryB)
)
->then
->boolean($autoloader->hasBaseDirectory($prefix))
->isTrue()
->array($autoloader->getBaseDirectories($prefix))
->isEqualTo([
$baseDirectoryB,
$baseDirectoryA
]);
}
public function case_add_namespace_append()
{
$this
->given(
$autoloader = new SUT(),
$prefix = 'Foo\Bar\\',
$baseDirectoryA = 'Source/Foo/Bar/',
$baseDirectoryB = 'Source/Foo/Bar/'
)
->when(
$autoloader->addNamespace($prefix, $baseDirectoryA),
$result = $autoloader->addNamespace($prefix, $baseDirectoryB)
)
->then
->boolean($autoloader->hasBaseDirectory($prefix))
->isTrue()
->array($autoloader->getBaseDirectories($prefix))
->isEqualTo([
$baseDirectoryA,
$baseDirectoryB
]);
}
public function case_add_namespace_with_invalid_prefix()
{
$this
->given(
$autoloader = new SUT(),
$prefix = '\\\\Foo\Bar',
$baseDirectory = 'Source/Foo/Bar/'
)
->when($result = $autoloader->addNamespace($prefix, $baseDirectory))
->then
->boolean($autoloader->hasBaseDirectory('Foo\Bar\\'))
->isTrue()
->array($autoloader->getBaseDirectories('Foo\Bar\\'))
->isEqualTo([$baseDirectory]);
}
public function case_add_namespace_with_invalid_base_directory()
{
$this
->given(
$autoloader = new SUT(),
$prefix = 'Foo\Bar\\',
$baseDirectory = 'Source/Foo/Bar'
)
->when($result = $autoloader->addNamespace($prefix, $baseDirectory))
->then
->boolean($autoloader->hasBaseDirectory('Foo\Bar\\'))
->isTrue()
->array($autoloader->getBaseDirectories('Foo\Bar\\'))
->isEqualTo(['Source/Foo/Bar/']);
}
public function case_add_namespace_with_crazy_invalid_base_directory()
{
$this
->given(
$autoloader = new SUT(),
$prefix = 'Foo\Bar\\',
$baseDirectory = 'Source/Foo/Bar/////'
)
->when($result = $autoloader->addNamespace($prefix, $baseDirectory))
->then
->boolean($autoloader->hasBaseDirectory('Foo\Bar\\'))
->isTrue()
->array($autoloader->getBaseDirectories('Foo\Bar\\'))
->isEqualTo(['Source/Foo/Bar/']);
}
public function case_load()
{
$this
->given(
$autoloader = new \Mock\Hoa\Consistency\Autoloader(),
$autoloader->addNamespace('Foo\Bar\\', 'Source/Foo/Bar/'),
$this->calling($autoloader)->requireFile = function ($file) {
return $file;
}
)
->when($result = $autoloader->load('Foo\Bar\Baz\Qux'))
->then
->string($result)
->isEqualTo('Source/Foo/Bar/Baz/Qux.php');
}
public function case_load_invalid_entity()
{
$this
->given($autoloader = new SUT())
->when($result = $autoloader->load('Foo'))
->then
->variable($result)
->isNull();
}
public function case_load_flex_entity()
{
$self = $this;
$this
->given(
$autoloader = new \Mock\Hoa\Consistency\Autoloader(),
$autoloader->addNamespace('Foo\Bar\\', 'Source/Foo/'),
$this->calling($autoloader)->runAutoloaderStack = function ($entity) use ($self, &$called) {
$called = true;
$self
->string($entity)
->isEqualTo('Foo\Bar\Baz\Baz');
return;
},
$autoloader->register()
)
->when($result = $autoloader->load('Foo\Bar\Baz'))
->then
->variable($result)
->isNull()
->boolean($called)
->isTrue();
}
public function case_load_unmapped_flex_entity()
{
$self = $this;
$this
->given(
$autoloader = new \Mock\Hoa\Consistency\Autoloader(),
$this->calling($autoloader)->runAutoloaderStack = function ($entity) use ($self, &$called) {
$called = true;
return;
},
$autoloader->register()
)
->when($result = $autoloader->load('Foo\Bar\Baz'))
->then
->variable($result)
->isNull()
->variable($called)
->isNull();
}
public function case_require_existing_file()
{
$this
->given(
$autoloader = new SUT(),
$this->function->file_exists = true,
$constantName = 'HOA_TEST_' . uniqid(),
$filename = 'hoa://Test/Vfs/Foo?type=file',
file_put_contents($filename, 'when($result = $autoloader->requireFile($filename))
->then
->boolean($result)
->isTrue()
->string(constant($constantName))
->isEqualTo('BAR');
}
public function case_require_not_existing_file()
{
$this
->given(
$autoloader = new SUT(),
$this->function->file_exists = false
)
->when($result = $autoloader->requireFile('/hoa/flatland'))
->then
->boolean($result)
->isFalse();
}
public function case_has_not_base_directory()
{
$this
->given($autoloader = new SUT())
->when($result = $autoloader->hasBaseDirectory('foo'))
->then
->boolean($result)
->isFalse();
}
public function case_get_base_undeclared_namespace_prefix()
{
$this
->given($autoloader = new SUT())
->when($result = $autoloader->getBaseDirectories('foo'))
->then
->array($result)
->isEmpty();
}
public function case_dnew()
{
$this
->given($classname = 'Hoa\Consistency\Autoloader')
->when($result = SUT::dnew($classname))
->then
->object($result)
->isInstanceOf($classname);
}
public function case_dnew_unknown_class()
{
$this
->given($this->function->spl_autoload_call = null)
->exception(function () {
SUT::dnew('Foo');
})
->isInstanceOf('ReflectionException');
}
public function case_get_loaded_classes()
{
$this
->given(
$declaredClasses = get_declared_classes(),
$this->function->get_declared_classes = $declaredClasses
)
->when($result = SUT::getLoadedClasses())
->then
->array($result)
->isEqualTo($declaredClasses);
}
public function case_register()
{
$self = $this;
$this
->given($autoloader = new SUT())
->when($result = $autoloader->register())
->then
->boolean($result)
->isTrue()
->array($autoloader->getRegisteredAutoloaders())
->isEqualTo(spl_autoload_functions());
}
public function case_unregister()
{
$this
->given(
$autoloader = new SUT(),
$oldRegisteredAutoloaders = $autoloader->getRegisteredAutoloaders()
)
->when($result = $autoloader->register())
->then
->boolean($result)
->isTrue()
->integer(count($autoloader->getRegisteredAutoloaders()))
->isEqualTo(count($oldRegisteredAutoloaders) + 1)
->when($result = $autoloader->unregister())
->then
->boolean($result)
->isTrue()
->array($autoloader->getRegisteredAutoloaders())
->isEqualTo($oldRegisteredAutoloaders);
}
}
Test/Unit/Consistency.php 0000666 00000023406 13371317540 0011431 0 ustar 00 given(
$this->function->class_exists = $class,
$this->function->interface_exists = $interface,
$this->function->trait_exists = $trait
)
->when($result = SUT::entityExists('foo'))
->then
->boolean($result)
->isTrue();
}
public function case_entity_exists_with_class()
{
return $this->_entity_exists_with_xxx(true, false, false);
}
public function case_entity_exists_with_interface()
{
return $this->_entity_exists_with_xxx(false, true, false);
}
public function case_entity_exists_with_trait()
{
return $this->_entity_exists_with_xxx(false, false, true);
}
public function case_entity_does_not_exists()
{
$this
->given(
$this->function->class_exists = false,
$this->function->interface_exists = false,
$this->function->trait_exists = false
)
->when($result = SUT::entityExists('foo'))
->then
->boolean($result)
->isFalse();
}
public function case_get_entity_shortest_name()
{
$this
->when($result = SUT::getEntityShortestName('Foo\Bar\Bar'))
->then
->string($result)
->isEqualTo('Foo\Bar');
}
public function case_get_entity_shortest_name_with_already_the_shortest()
{
$this
->when($result = SUT::getEntityShortestName('Foo\Bar'))
->then
->string($result)
->isEqualTo('Foo\Bar');
}
public function case_get_entity_shortest_name_with_no_namespace()
{
$this
->when($result = SUT::getEntityShortestName('Foo'))
->then
->string($result)
->isEqualTo('Foo');
}
public function case_is_keyword()
{
$this
->given(
$keywords = [
'__HALT_COMPILER',
'abstract',
'and',
'array',
'as',
'bool',
'break',
'callable',
'case',
'catch',
'class',
'clone',
'const',
'continue',
'declare',
'default',
'die',
'do',
'echo',
'else',
'elseif',
'empty',
'enddeclare',
'endfor',
'endforeach',
'endif',
'endswitch',
'endwhile',
'eval',
'exit',
'extends',
'false',
'final',
'float',
'for',
'foreach',
'function',
'global',
'goto',
'if',
'implements',
'include',
'include_once',
'instanceof',
'insteadof',
'int',
'interface',
'isset',
'list',
'mixed',
'namespace',
'new',
'null',
'numeric',
'object',
'or',
'print',
'private',
'protected',
'public',
'require',
'require_once',
'resource',
'return',
'static',
'string',
'switch',
'throw',
'trait',
'true',
'try',
'unset',
'use',
'var',
'void',
'while',
'xor',
'yield',
'__CLASS__',
'__DIR__',
'__FILE__',
'__FUNCTION__',
'__LINE__',
'__METHOD__',
'__NAMESPACE__',
'__TRAIT__'
]
)
->when(function () use ($keywords) {
foreach ($keywords as $keyword) {
$this
->boolean(SUT::isKeyword($keyword))
->isTrue();
}
});
}
public function case_is_identifier()
{
$this
->given($_identifier = $this->realdom->regex('#^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x80-\xff]*$#'))
->when(function () use ($_identifier) {
foreach ($this->sampleMany($_identifier, 1000) as $identifier) {
$this
->boolean(SUT::isIdentifier($identifier))
->isTrue();
}
});
}
public function case_register_shutdown_function()
{
$self = $this;
$this
->given(
$callable = function () {
},
$this->function->register_shutdown_function = function ($_callable) use (&$called, $self, &$callable) {
$called = true;
$self
->variable($_callable)
->isEqualTo($callable);
return true;
}
)
->when($result = SUT::registerShutdownFunction($callable))
->then
->boolean($result)
->isTrue();
}
public function case_get_php_binary_with_constant()
{
$this
->given($this->constant->PHP_BINARY = '/foo/php')
->when($result = SUT::getPHPBinary())
->then
->string($result)
->isEqualTo('/foo/php');
}
public function case_get_php_binary_with_server()
{
$this
->given(
$this->function->defined = false,
$_SERVER['_'] = '/bar/php'
)
->when($result = SUT::getPHPBinary())
->then
->string($result)
->isEqualTo('/bar/php');
}
public function case_get_php_binary_with_bin_directory()
{
unset($_SERVER['_']);
$this
->given(
$this->function->defined = false,
$this->function->file_exists = true,
$this->function->realpath = '/baz/php'
)
->when($result = SUT::getPHPBinary())
->then
->string($result)
->isEqualTo('/baz/php');
}
public function case_uuid()
{
$this
->given($this->function->mt_rand = 42)
->when($result = SUT::uuid())
->then
->string($result)
->isEqualTo('002a002a-002a-402a-802a-002a002a002a');
}
public function case_uuid_all_differents()
{
$this
->when(function () {
$uuids = [];
for ($i = 0; $i < 10000; ++$i) {
$uuids[] = SUT::uuid();
}
$this
->integer(count($uuids))
->isEqualTo(count(array_unique($uuids)));
});
}
}
Test/Unit/Exception.php 0000666 00000004220 13371317540 0011057 0 ustar 00 when($result = new SUT('foo', 0))
->then
->object($result)
->isInstanceOf('Hoa\Exception\Exception');
}
}
Test/Unit/Xcallable.php 0000666 00000026645 13371317540 0011027 0 ustar 00 when($result = new SUT('strtoupper'))
->then
->string($result('foo'))
->isEqualTo('FOO')
->string($result->getValidCallback())
->isEqualTo('strtoupper')
->string($result->getHash())
->isEqualTo('function#strtoupper')
->isEqualTo($result . '')
->object($reflection = $result->getReflection())
->isInstanceOf('ReflectionFunction')
->string($reflection->getName())
->isEqualTo('strtoupper');
}
public function case_form_class___method()
{
$this
->when($result = new SUT(__CLASS__ . '::strtoupper'))
->then
->string($result('foo'))
->isEqualTo('FOO')
->array($result->getValidCallback())
->isEqualTo([__CLASS__, 'strtoupper'])
->string($result->getHash())
->isEqualTo('class#' . __CLASS__ . '::strtoupper')
->isEqualTo($result . '')
->object($reflection = $result->getReflection())
->isInstanceOf('ReflectionMethod')
->string($reflection->getName())
->isEqualTo('strtoupper');
}
public function case_form_class_method()
{
$this
->when($result = new SUT(__CLASS__, 'strtoupper'))
->then
->string($result('foo'))
->isEqualTo('FOO')
->array($result->getValidCallback())
->isEqualTo([__CLASS__, 'strtoupper'])
->string($result->getHash())
->isEqualTo('class#' . __CLASS__ . '::strtoupper')
->isEqualTo($result . '')
->object($reflection = $result->getReflection())
->isInstanceOf('ReflectionMethod')
->string($reflection->getName())
->isEqualTo('strtoupper');
}
public function case_form_object_method()
{
$this
->when($result = new SUT($this, 'strtolower'))
->then
->string($result('FOO'))
->isEqualTo('foo')
->array($result->getValidCallback())
->isEqualTo([$this, 'strtolower'])
->string($result->getHash())
->matches(
'/^object\([^:]+\)#' .
preg_quote(__CLASS__) .
'::strtolower$/'
)
->isEqualTo($result . '')
->object($reflection = $result->getReflection())
->isInstanceOf('ReflectionMethod')
->string($reflection->getName())
->isEqualTo('strtolower');
}
public function case_form_object_invoke()
{
$this
->when($result = new SUT($this))
->then
->string($result('foo'))
->isEqualTo('FOO')
->array($result->getValidCallback())
->isEqualTo([$this, '__invoke'])
->string($result->getHash())
->matches(
'/^object\([^:]+\)#' .
preg_quote(__CLASS__) .
'::__invoke$/'
)
->isEqualTo($result . '')
->object($reflection = $result->getReflection())
->isInstanceOf('ReflectionMethod')
->string($reflection->getName())
->isEqualTo('__invoke');
}
public function case_form_closure()
{
$this
->given(
$closure = function ($string) {
return strtoupper($string);
}
)
->when($result = new SUT($closure))
->then
->string($result('foo'))
->isEqualTo('FOO')
->object($result->getValidCallback())
->isIdenticalTo($closure)
->string($result->getHash())
->matches('/^closure\([^:]+\)$/')
->isEqualTo($result . '')
->object($reflection = $result->getReflection())
->isInstanceOf('ReflectionFunction')
->string($reflection->getName())
->isEqualTo('Hoa\Consistency\Test\Unit\{closure}');
}
public function case_form_array_of_class_method()
{
$this
->when($result = new SUT([__CLASS__, 'strtoupper']))
->then
->string($result('foo'))
->isEqualTo('FOO')
->array($result->getValidCallback())
->isEqualTo([__CLASS__, 'strtoupper'])
->string($result->getHash())
->isEqualTo('class#' . __CLASS__ . '::strtoupper')
->isEqualTo($result . '')
->object($reflection = $result->getReflection())
->isInstanceOf('ReflectionMethod')
->string($reflection->getName())
->isEqualTo('strtoupper');
}
public function case_form_array_of_object_method()
{
$this
->when($result = new SUT([$this, 'strtolower']))
->then
->string($result('FOO'))
->isEqualTo('foo')
->array($result->getValidCallback())
->isEqualTo([$this, 'strtolower'])
->string($result->getHash())
->matches(
'/^object\([^:]+\)#' .
preg_quote(__CLASS__) .
'::strtolower$/'
)
->isEqualTo($result . '')
->object($reflection = $result->getReflection())
->isInstanceOf('ReflectionMethod')
->string($reflection->getName())
->isEqualTo('strtolower');
}
public function case_form_able_not_a_string()
{
$this
->exception(function () {
new SUT(__CLASS__, 123);
})
->isInstanceOf('Hoa\Consistency\Exception');
}
public function case_form_function_not_defined()
{
$this
->exception(function () {
new SUT('__hoa_test_undefined_function__');
})
->isInstanceOf('Hoa\Consistency\Exception');
}
public function case_form_able_cannot_be_deduced()
{
$this
->given($this->function->method_exists = false)
->exception(function () {
new SUT($this);
})
->isInstanceOf('Hoa\Consistency\Exception');
}
public function case_invoke()
{
$this
->given(
$callable = new SUT(
function ($x, $y, $z) {
return [$x, $y, $z];
}
)
)
->when($result = $callable(7, [4.2], 'foo'))
->then
->array($result)
->isEqualTo([7, [4.2], 'foo']);
}
public function case_distribute_arguments()
{
$this
->given(
$callable = new SUT(
function ($x, $y, $z) {
return [$x, $y, $z];
}
)
)
->when($result = $callable->distributeArguments([7, [4.2], 'foo']))
->then
->array($result)
->isEqualTo([7, [4.2], 'foo']);
}
protected function _get_valid_callback_stream_xxx($argument, $method)
{
$this
->given(
$stream = new \Mock\Hoa\Stream\IStream\Out(),
$arguments = [$argument],
$xcallable = new SUT($stream)
)
->when($result = $xcallable->getValidCallback($arguments))
->then
->array($result)
->isEqualTo([$stream, $method]);
}
public function case_get_valid_callback_stream_character()
{
return $this->_get_valid_callback_stream_xxx('f', 'writeCharacter');
}
public function case_get_valid_callback_stream_string()
{
return $this->_get_valid_callback_stream_xxx('foo', 'writeString');
}
public function case_get_valid_callback_stream_boolean()
{
return $this->_get_valid_callback_stream_xxx(true, 'writeBoolean');
}
public function case_get_valid_callback_stream_integer()
{
return $this->_get_valid_callback_stream_xxx(7, 'writeInteger');
}
public function case_get_valid_callback_stream_array()
{
return $this->_get_valid_callback_stream_xxx([4, 2], 'writeArray');
}
public function case_get_valid_callback_stream_float()
{
return $this->_get_valid_callback_stream_xxx(4.2, 'writeFloat');
}
public function case_get_valid_callback_stream_other()
{
return $this->_get_valid_callback_stream_xxx($this, 'writeAll');
}
public static function strtoupper($string)
{
return strtoupper($string);
}
public function strtolower($string)
{
return strtolower($string);
}
public function __invoke($string)
{
return strtoupper($string);
}
public function __toString()
{
return 'hello';
}
}
Xcallable.php 0000666 00000021144 13371317540 0007156 0 ustar 00 method or
* closure, they all have the same behaviour. This callable is an extension of
* native PHP callable (aka callback) to integrate Hoa's structures.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class Xcallable
{
/**
* Callback, with the PHP format.
*
* @var mixed
*/
protected $_callback = null;
/**
* Callable hash.
*
* @var string
*/
protected $_hash = null;
/**
* Build a callback.
* Accepted forms:
* * `'function'`,
* * `'class::method'`,
* * `'class', 'method'`,
* * `$object, 'method'`,
* * `$object, ''`,
* * `function (…) { … }`,
* * `['class', 'method']`,
* * `[$object, 'method']`.
*
* @param mixed $call First callable part.
* @param mixed $able Second callable part (if needed).
*/
public function __construct($call, $able = '')
{
if ($call instanceof \Closure) {
$this->_callback = $call;
return;
}
if (!is_string($able)) {
throw new Exception(
'Bad callback form; the able part must be a string.',
0
);
}
if ('' === $able) {
if (is_string($call)) {
if (false === strpos($call, '::')) {
if (!function_exists($call)) {
throw new Exception(
'Bad callback form; function %s does not exist.',
1,
$call
);
}
$this->_callback = $call;
return;
}
list($call, $able) = explode('::', $call);
} elseif (is_object($call)) {
if ($call instanceof Stream\IStream\Out) {
$able = null;
} elseif (method_exists($call, '__invoke')) {
$able = '__invoke';
} else {
throw new Exception(
'Bad callback form; an object but without a known ' .
'method.',
2
);
}
} elseif (is_array($call) && isset($call[0])) {
if (!isset($call[1])) {
return $this->__construct($call[0]);
}
return $this->__construct($call[0], $call[1]);
} else {
throw new Exception(
'Bad callback form.',
3
);
}
}
$this->_callback = [$call, $able];
return;
}
/**
* Call the callable.
*
* @param ...
* @return mixed
*/
public function __invoke()
{
$arguments = func_get_args();
$valid = $this->getValidCallback($arguments);
return call_user_func_array($valid, $arguments);
}
/**
* Distribute arguments according to an array.
*
* @param array $arguments Arguments.
* @return mixed
*/
public function distributeArguments(array $arguments)
{
return call_user_func_array([$this, '__invoke'], $arguments);
}
/**
* Get a valid callback in the PHP meaning.
*
* @param array &$arguments Arguments (could determine method on an
* object if not precised).
* @return mixed
*/
public function getValidCallback(array &$arguments = [])
{
$callback = $this->_callback;
$head = null;
if (isset($arguments[0])) {
$head = &$arguments[0];
}
// If method is undetermined, we find it (we understand event bucket and
// stream).
if (null !== $head &&
is_array($callback) &&
null === $callback[1]) {
if ($head instanceof Event\Bucket) {
$head = $head->getData();
}
switch ($type = gettype($head)) {
case 'string':
if (1 === strlen($head)) {
$method = 'writeCharacter';
} else {
$method = 'writeString';
}
break;
case 'boolean':
case 'integer':
case 'array':
$method = 'write' . ucfirst($type);
break;
case 'double':
$method = 'writeFloat';
break;
default:
$method = 'writeAll';
$head = $head . "\n";
}
$callback[1] = $method;
}
return $callback;
}
/**
* Get hash.
* Will produce:
* * function#…;
* * class#…::…;
* * object(…)#…::…;
* * closure(…).
*
* @return string
*/
public function getHash()
{
if (null !== $this->_hash) {
return $this->_hash;
}
$_ = &$this->_callback;
if (is_string($_)) {
return $this->_hash = 'function#' . $_;
}
if (is_array($_)) {
return
$this->_hash =
(is_object($_[0])
? 'object(' . spl_object_hash($_[0]) . ')' .
'#' . get_class($_[0])
: 'class#' . $_[0]) .
'::' .
(null !== $_[1]
? $_[1]
: '???');
}
return $this->_hash = 'closure(' . spl_object_hash($_) . ')';
}
/**
* Get appropriated reflection instance.
*
* @param ...
* @return \Reflector
*/
public function getReflection()
{
$arguments = func_get_args();
$valid = $this->getValidCallback($arguments);
if (is_string($valid)) {
return new \ReflectionFunction($valid);
}
if ($valid instanceof \Closure) {
return new \ReflectionFunction($valid);
}
if (is_array($valid)) {
if (is_string($valid[0])) {
if (false === method_exists($valid[0], $valid[1])) {
return new \ReflectionClass($valid[0]);
}
return new \ReflectionMethod($valid[0], $valid[1]);
}
$object = new \ReflectionObject($valid[0]);
if (null === $valid[1]) {
return $object;
}
return $object->getMethod($valid[1]);
}
}
/**
* Return the hash.
*
* @return string
*/
public function __toString()
{
return $this->getHash();
}
}
composer.json 0000666 00000002453 13371317540 0007302 0 ustar 00 {
"name" : "hoa/consistency",
"description": "The Hoa\\Consistency library.",
"type" : "library",
"keywords" : ["library", "consistency", "autoloader", "entity", "flex",
"keyword", "callable"],
"homepage" : "https://hoa-project.net/",
"license" : "BSD-3-Clause",
"authors" : [
{
"name" : "Ivan Enderlin",
"email": "ivan.enderlin@hoa-project.net"
},
{
"name" : "Hoa community",
"homepage": "https://hoa-project.net/"
}
],
"support": {
"email" : "support@hoa-project.net",
"irc" : "irc://chat.freenode.net/hoaproject",
"forum" : "https://users.hoa-project.net/",
"docs" : "https://central.hoa-project.net/Documentation/Library/Consistency",
"source": "https://central.hoa-project.net/Resource/Library/Consistency"
},
"require": {
"php" : ">=5.5.0",
"hoa/exception": "~1.0"
},
"require-dev": {
"hoa/stream": "~1.0",
"hoa/test" : "~2.0"
},
"autoload": {
"psr-4": {
"Hoa\\Consistency\\": "."
},
"files": ["Prelude.php"]
},
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
}
}
}