proof of concept

This commit is contained in:
Kacper Donat 2018-02-28 21:37:37 +01:00
commit 40f8bfeab8
9 changed files with 276 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Created by .ignore support plugin (hsz.mobi)
/.idea/
# PHP
/vendor/
/node_modules/
test.*.php

26
composer.json Normal file
View File

@ -0,0 +1,26 @@
{
"name": "kadet/predicate",
"description": "Functional library for PHP",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Kacper Donat",
"email": "kadet1090@gmail.com"
}
],
"require": {
"php": ">=7.1"
},
"require-dev": {
"phpunit/phpunit": "^7.0"
},
"autoload": {
"psr-4": {
"Kadet\\Functional\\": "./src/"
},
"files": [
"./src/functions.php"
]
}
}

21
src/Predicate.php Normal file
View File

@ -0,0 +1,21 @@
<?php
/**
* Copyright 2018 Kacper Donat
*
* @author Kacper "Kadet" Donat <kacper@kadet.net>
*
* Full license available in separate LICENSE file
*/
namespace Kadet\Functional;
interface Predicate
{
public function __invoke(...$args): bool;
public function not(): Predicate;
public function and(...$predicate): Predicate;
public function or(...$predicate): Predicate;
}

View File

@ -0,0 +1,34 @@
<?php
/**
* Copyright 2018 Kacper Donat
*
* @author Kacper "Kadet" Donat <kacper@kadet.net>
*
* Full license available in separate LICENSE file
*/
namespace Kadet\Functional\Predicate;
use Kadet\Functional\Predicate;
use function Kadet\Functional\predicate;
abstract class AbstractPredicate implements Predicate
{
public function not(): Predicate
{
return new NegatedPredicate($this);
}
public function and(...$predicate): Predicate
{
array_unshift($predicate, $this);
return new AllOfPredicate(...array_map(predicate::class, $predicate));
}
public function or(...$predicate): Predicate
{
array_unshift($predicate, $this);
return new AnyOfPredicate(...array_map(predicate::class, $predicate));
}
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Copyright 2018 Kacper Donat
*
* @author Kacper "Kadet" Donat <kacper@kadet.net>
*
* Full license available in separate LICENSE file
*/
namespace Kadet\Functional\Predicate;
use Kadet\Functional\Predicate;
class AllOfPredicate extends AbstractPredicate
{
/**
* Array of predicates to check if all are satisfied.
* @var \Kadet\Functional\Predicate[]
*/
private $predicates = [];
public function __construct(Predicate ...$predicates)
{
$this->predicates = $predicates;
}
public function and(...$predicate): Predicate
{
$predicate = array_merge($this->predicates, $predicate);
return parent::and($predicate);
}
public function __invoke(...$args): bool
{
foreach ($this->predicates as $predicate) {
if (!$predicate(...$args)) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,45 @@
<?php
/**
* Copyright 2018 Kacper Donat
*
* @author Kacper "Kadet" Donat <kacper@kadet.net>
*
* Full license available in separate LICENSE file
*/
namespace Kadet\Functional\Predicate;
use Kadet\Functional\Predicate;
class AnyOfPredicate extends AbstractPredicate
{
/**
* Array of predicates to check if all are satisfied.
* @var \Kadet\Functional\Predicate[]
*/
private $predicates = [];
public function __construct(Predicate ...$predicates)
{
$this->predicates = $predicates;
}
public function or(...$predicate): Predicate
{
$predicate = array_merge($this->predicates, $predicate);
return parent::or($predicate);
}
public function __invoke(...$args): bool
{
foreach ($this->predicates as $predicate) {
if ($predicate(...$args)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,34 @@
<?php
/**
* Copyright 2018 Kacper Donat
*
* @author Kacper "Kadet" Donat <kacper@kadet.net>
*
* Full license available in separate LICENSE file
*/
namespace Kadet\Functional\Predicate;
class ClosurePredicate extends AbstractPredicate
{
/**
* @var \Closure
*/
private $closure;
/**
* ClosurePredicate constructor.
*
* @param \Closure $closure
*/
public function __construct(\Closure $closure)
{
$this->closure = $closure;
}
public function __invoke(...$args): bool
{
return ($this->closure)(...$args);
}
}

View File

@ -0,0 +1,38 @@
<?php
/**
* Copyright 2018 Kacper Donat
*
* @author Kacper "Kadet" Donat <kacper@kadet.net>
*
* Full license available in separate LICENSE file
*/
namespace Kadet\Functional\Predicate;
use Kadet\Functional\Predicate;
class NegatedPredicate extends AbstractPredicate
{
/**
* Predicate to be negated.
* @var \Kadet\Functional\Predicate
*/
private $negated;
public function __construct(Predicate $predicate)
{
$this->negated = $predicate;
}
public function not(): Predicate
{
// negation of negations is plain value
return $this->negated;
}
public function __invoke(...$args): bool
{
return !($this->negated)(...$args);
}
}

26
src/functions.php Normal file
View File

@ -0,0 +1,26 @@
<?php
namespace Kadet\Functional;
use Kadet\Functional\Predicate\ClosurePredicate;
/**
* @param $predicate
*
* @return \Kadet\Functional\Predicate
* @throws \Exception
*/
function predicate($predicate): Predicate
{
if ($predicate instanceof Predicate) {
return $predicate;
}
if (is_callable($predicate)) {
return new ClosurePredicate(\Closure::fromCallable($predicate));
}
// todo better exception
throw new \Exception();
}