Commit 026682b0 authored by Guillaume Perréal's avatar Guillaume Perréal
Browse files

First version.

parents
root=true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.{js,ts,yml,yaml,json,neon}]
indent_size = 2
[Makefile]
indent_style = tab
/vendor/
/.idea/
/.php_cs*cache
.phpunit.result.cache
stages:
- vendor
- test
.php: &php
tags:
- docker
image: php:${PHP_VERSION}-cli-stretch
variables:
COMPOSER_HOME: "$CI_PROJECT_DIR/.composer"
COMPOSER_NO_INTERACTION: "1"
COMPOSER_ALLOW_SUPERUSER: "1"
COMPOSER_INSTALLER_COMMITID: "5eb0614d3fa7130b363698d3dca52c619b463615"
PHP_VERSION: "7.1"
before_script:
- apt-get update -yqq && apt-get install -yqq unzip git-core
- php -v
- curl -sSfL https://raw.githubusercontent.com/composer/getcomposer.org/${COMPOSER_INSTALLER_COMMITID}/web/installer | php -- --install-dir=/usr/local/bin --filename=composer --quiet
- composer -V
vendor:
<<: *php
stage: vendor
dependencies: []
script:
- composer global require hirak/prestissimo --quiet
- composer install --no-suggest --no-progress
artifacts:
name: "$CI_JOB_NAME"
paths:
- vendor
expire_in: 4 hours
cache:
key: "$CI_BUILD_REF_NAME-composer"
paths:
- "$COMPOSER_HOME"
.php-test: &php-test
<<: *php
stage: test
dependencies:
- vendor
script: composer ${CI_JOB_NAME}
phploc: { <<: *php-test }
test:lint: { <<: *php-test }
test:phpcpd: { <<: *php-test }
test:phpmd: { <<: *php-test }
test:phpstan: { <<: *php-test }
test:phpunit: { <<: *php-test }
test:composer-validate: { <<: *php-test }
test:composer-require: { <<: *php-test }
test:security: { <<: *php-test }
<?php
$finder = PhpCsFixer\Finder::create()
->exclude('vendor')
->files()
->name('*.php')
->in('.');
return Irstea\CS\Config::create()
->setIndent(' ')
->setLineEnding("\n")
->setFinder($finder);
{
"name": "irstea/maybe",
"description": "Implémentation du type Maybe pour PHP.",
"type": "library",
"license": "MIT",
"keywords": [
"maybe"
],
"authors": [
{
"name": "Guillaume Perréal",
"role": "Developer"
}
],
"homepage": "https://gitlab.irstea.fr/pole-is/bundles/maybe",
"support": {
"email": "dsi.poleis@lists.irstea.fr",
"issues": "https://gitlab.irstea.fr/pole-is/bundles/maybe/issues",
"source": "https://gitlab.irstea.fr/pole-is/bundles/maybe/tree/master"
},
"minimum-stability": "stable",
"prefer-stable": true,
"config": {
"preferred-install": "dist",
"sort-packages": true
},
"require": {
"php": "^7.1",
"beberlei/assert": "^3.2"
},
"require-dev": {
"irstea/php-cs-fixer-config": "^1.0",
"irstea/phpmd-config": "^1.0",
"irstea/phpstan-config": "^1.0",
"jakub-onderka/php-parallel-lint": "^1.0",
"maglnet/composer-require-checker": "^2.0",
"phploc/phploc": "^5.0",
"phpunit/phpunit": "^8.1",
"roave/security-advisories": "dev-master",
"sebastian/phpcpd": "^4.1",
"sensiolabs/security-checker": "^5.0"
},
"autoload": {
"psr-4": {
"Irstea\\Maybe\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Irstea\\Maybe\\Tests\\": "tests/"
}
},
"scripts": {
"phploc": "vendor/bin/phploc src tests",
"fix-cs": "vendor/bin/php-cs-fixer fix",
"test": [
"@test:lint",
"@test:phpcpd",
"@test:phpmd",
"@test:phpstan",
"@test:phpunit",
"@test:composer-validate",
"@test:composer-require",
"@test:security"
],
"test:lint": [
"vendor/bin/parallel-lint src tests",
"vendor/bin/php-cs-fixer fix --dry-run"
],
"test:phpcpd": "vendor/bin/phpcpd --fuzzy src tests",
"test:phpmd": "vendor/bin/phpmd src,tests text ./phpmd-ruleset.xml",
"test:phpstan": "vendor/bin/phpstan analyse --ansi --no-progress src tests",
"test:phpunit": "vendor/bin/phpunit",
"test:composer-validate": "composer validate",
"test:composer-require": "vendor/bin/composer-require-checker check",
"test:security": "test '!' -s composer.lock || vendor/bin/security-checker security:check"
},
"scripts-descriptions": {
"fix-cs": "Applique les corrections automatiques de style.",
"phploc": "Calcule des stats sur les sources.",
"test": "Exécute tous les tests en série.",
"test:lint": "Vérifie la syntaxe et le style des sources.",
"test:phpcpd": "Détecte les blocs de code similaires.",
"test:phpmd": "Détecte des défauts de codage.",
"test:phpstan": "Détecte des erreurs de programmation grâce au typage.",
"test:phpunit": "Exécute les tests dynamiques.",
"test:composer-validate": "Vérifie la validité du composer.json.",
"test:composer-require": "Vérifie que les sources n'utilise que des librairies listés dans les dépendances directes.",
"test:security": "Vérifie qu'aucun paquet utilisé n'ait de faille de sécurité signalée."
}
}
<?xml version="1.0"?>
<ruleset name="SYGADE PHPMD rule set"
xmlns="http://pmd.sf.net/ruleset/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
<rule ref="vendor/irstea/phpmd-config/strict.xml"/>
</ruleset>
includes:
- vendor/irstea/phpstan-config/strict.neon
- vendor/irstea/phpstan-config/phpunit.neon
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/8.1/phpunit.xsd"
bootstrap="vendor/autoload.php"
executionOrder="depends,defects"
forceCoversAnnotation="true"
beStrictAboutCoversAnnotation="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
verbose="true">
<testsuites>
<testsuite name="default">
<directory suffix="Test.php">tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src</directory>
</whitelist>
</filter>
</phpunit>
<?php declare(strict_types=1);
/*
* Create and customize a file named .docheader at the root of the project to change this message.
*/
namespace Irstea\Maybe\Exception;
/**
* Class NothingException.
*/
class NothingException extends \RuntimeException
{
}
<?php declare(strict_types=1);
/*
* Create and customize a file named .docheader at the root of the project to change this message.
*/
namespace Irstea\Maybe;
use Assert\Assertion;
use Irstea\Maybe\Exception\NothingException;
use Irstea\Maybe\Maybe\Just;
use Irstea\Maybe\Maybe\Nothing;
/**
* Class Maybe.
*/
abstract class Maybe implements \IteratorAggregate
{
/**
* @param callable $project
*
* @return Maybe
*/
abstract public function map(callable $project): Maybe;
/**
* @param callable $predicate
*
* @return Maybe
*/
abstract public function filter(callable $predicate): Maybe;
/**
* @param Maybe $other
*
* @return Maybe
*/
abstract public function concat(Maybe $other): Maybe;
/**
* @return \Iterator
*/
abstract public function getIterator(): \Iterator;
/**
* @param callable $withJust
* @param callable $withNothing
*
* @return mixed
*/
abstract public function resolve(callable $withJust, callable $withNothing);
/**
* @param $default
*
* @return mixed
*/
abstract public function valueOr($default);
/**
* @return mixed
*/
abstract public function value();
/**
* @return mixed
*/
abstract public function toArray(): array;
/**
* @param Maybe|mixed $value
*
* @return Maybe
*/
public static function just($value): Maybe
{
return $value instanceof self ? $value : Just::from($value);
}
/**
* @return Maybe
*/
public static function nothing(): Maybe
{
return Nothing::instance();
}
/**
* @param Maybe[] $maybes
*
* @return array
*/
public static function some(array $maybes): array
{
Assertion::allIsInstanceOf($maybes, self::class);
return array_merge(
...array_map(
static function (Maybe $maybe) {
return $maybe->toArray();
},
$maybes
)
);
}
/**
* @param callable $generator
*/
public static function run(callable $generator): Maybe
{
$gen = $generator();
if (!$gen instanceof \Generator) {
return Maybe::just($gen);
}
try {
$gen->rewind();
$maybe = $gen->current();
while ($gen->valid()) {
try {
$maybe = $gen->send($maybe->value());
} catch (NothingException $ex) {
$gen->throw($ex);
}
}
return Maybe::just($gen->getReturn());
} catch (NothingException $ex) {
return Nothing::instance();
}
}
}
<?php declare(strict_types=1);
/*
* Create and customize a file named .docheader at the root of the project to change this message.
*/
namespace Irstea\Maybe\Maybe;
use Irstea\Maybe\Maybe;
/**
* Class Just.
*/
final class Just extends Maybe
{
/** @var mixed */
private $value;
/**
* Just constructor.
*
* @param $value
*/
private function __construct($value)
{
$this->value = $value;
}
/**
* {@inheritdoc}
*/
public function map(callable $project): Maybe
{
return Maybe::just($project($this->value()));
}
/**
* {@inheritdoc}
*/
public function filter(callable $predicate): Maybe
{
return $predicate($this->value) ? $this : Maybe::nothing();
}
/**
* {@inheritdoc}
*/
public function concat(Maybe $other): Maybe
{
return $this;
}
/**
* {@inheritdoc}
*/
public function getIterator(): \Iterator
{
return new \ArrayIterator([$this->value]);
}
/**
* {@inheritdoc}
*/
public function resolve(callable $withJust, callable $withNothing)
{
return $withJust($this->value);
}
/**
* {@inheritdoc}
*/
public function valueOr($default)
{
return $this->value;
}
/**
* {@inheritdoc}
*/
public function value()
{
return $this->value;
}
/**
* {@inheritdoc}
*/
public function toArray(): array
{
return [$this->value];
}
/**
* @param mixed $value
*
* @internal
*
* @return Just
*/
public static function from($value): Just
{
return new Just($value);
}
}
<?php declare(strict_types=1);
/*
* Create and customize a file named .docheader at the root of the project to change this message.
*/
namespace Irstea\Maybe\Maybe;
use Irstea\Maybe\Exception\NothingException;
use Irstea\Maybe\Maybe;
/**
* Class Nothing.
*/
final class Nothing extends Maybe
{
private function __construct()
{
}
/**
* {@inheritdoc}
*/
public function map(callable $project): Maybe
{
return $this;
}
/**
* {@inheritdoc}
*/
public function filter(callable $predicate): Maybe
{
return $this;
}
/**
* {@inheritdoc}
*/
public function concat(Maybe $other): Maybe
{
return $other;
}
/**
* {@inheritdoc}
*/
public function getIterator(): \Iterator
{
return new \EmptyIterator();
}
/**
* {@inheritdoc}
*/
public function resolve(callable $withJust, callable $withNothing)
{
return $withNothing();
}
/**
* {@inheritdoc}
*/
public function valueOr($default)
{
return $default;
}
/**
* {@inheritdoc}
*/
public function value()
{
throw new NothingException('Nothing has no value');
}
/**
* {@inheritdoc}
*/
public function toArray(): array
{
return [];
}
/**
* Return the Nothing singleton.
*
* @internal
*
* @return Nothing
*/
public static function instance(): Nothing
{
/** @var Nothing|null $instance */
static $instance;
if (!$instance) {
$instance = new Nothing();
}
return $instance;
}
}
<?php declare(strict_types=1);
/*
* Create and customize a file named .docheader at the root of the project to change this message.
*/
namespace Irstea\Maybe\Tests\Maybe;
use Irstea\Maybe\Maybe\Just;
use PHPUnit\Framework\TestCase;
/**
* Class JustTest.
*/
class JustTest extends TestCase
{
public function testMap()
{
self::assertEquals(
10,
Just::from(2)->map(
function ($value) {
return $value + 8;
}
)->value()
);
}
public function testResolve()
{
self::assertEquals(
10,
Just::from(2)->resolve(
function ($value) {
return $value + 8;
},
function () {
return 0;
}
)
);
}
public function testToArray()
{
self::assertEquals([2], Just::from(2)->toArray());
}
public function testValue()
{
self::assertEquals(
2,
Just::from(2)->value()
);
}
public function testFilterAccepted()
{
self::assertEquals([2], Just::from(2)->filter('is_integer')->toArray());
}
public function testFilterRejected()
{
self::assertNotEquals([2], Just::from(2)->filter('is_string')->toArray());
}
public function testValueOr()
{