mirror of
https://github.com/Karaka-Management/phpOMS.git
synced 2026-02-11 06:28:40 +00:00
Merge branch 'develop'
This commit is contained in:
commit
b9aec88a4b
|
|
@ -110,7 +110,7 @@ final class Kmeans
|
||||||
* Generate the clusters of the points
|
* Generate the clusters of the points
|
||||||
*
|
*
|
||||||
* @param PointInterface[] $points Points to cluster
|
* @param PointInterface[] $points Points to cluster
|
||||||
* @param int<0, max> $clusters Amount of clusters
|
* @param int<1, max> $clusters Amount of clusters
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*
|
*
|
||||||
|
|
@ -140,8 +140,7 @@ final class Kmeans
|
||||||
|
|
||||||
foreach ($clusterCenters as $center) {
|
foreach ($clusterCenters as $center) {
|
||||||
for ($i = 0; $i < $coordinates; ++$i) {
|
for ($i = 0; $i < $coordinates; ++$i) {
|
||||||
// @todo Invalid center coodinate value in like 5 % of the runs
|
$center->setCoordinate($i, $center->getCoordinate($i) / $center->group);
|
||||||
$center->setCoordinate($i, $center->getCoordinate($i) / ($center->group === 0 ? 1 : $center->group));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,7 +148,7 @@ final class Kmeans
|
||||||
foreach ($points as $point) {
|
foreach ($points as $point) {
|
||||||
$min = $this->nearestClusterCenter($point, $clusterCenters)[0];
|
$min = $this->nearestClusterCenter($point, $clusterCenters)[0];
|
||||||
|
|
||||||
if ($min !== $point->group) {
|
if ($clusters !== $point->group) {
|
||||||
++$changed;
|
++$changed;
|
||||||
$point->group = $min;
|
$point->group = $min;
|
||||||
}
|
}
|
||||||
|
|
@ -207,30 +206,41 @@ final class Kmeans
|
||||||
*/
|
*/
|
||||||
private function kpp(array $points, int $n) : array
|
private function kpp(array $points, int $n) : array
|
||||||
{
|
{
|
||||||
$clusters = [clone $points[\mt_rand(0, \count($points) - 1)]];
|
$clusters = [clone $points[\array_rand($points, 1)]];
|
||||||
$d = \array_fill(0, $n, 0.0);
|
|
||||||
|
$d = \array_fill(0, $n, 0.0);
|
||||||
|
|
||||||
for ($i = 1; $i < $n; ++$i) {
|
for ($i = 1; $i < $n; ++$i) {
|
||||||
$sum = 0;
|
$sum = 0;
|
||||||
|
|
||||||
foreach ($points as $key => $point) {
|
foreach ($points as $key => $point) {
|
||||||
$d[$key] = $this->nearestClusterCenter($point, \array_slice($clusters, 0, 5))[1];
|
$d[$key] = $this->nearestClusterCenter($point, $clusters)[1];
|
||||||
$sum += $d[$key];
|
$sum += $d[$key];
|
||||||
}
|
}
|
||||||
|
|
||||||
$sum *= \mt_rand(0, \mt_getrandmax()) / \mt_getrandmax();
|
$sum *= \mt_rand(0, \mt_getrandmax()) / \mt_getrandmax();
|
||||||
|
|
||||||
|
$found = false;
|
||||||
foreach ($d as $key => $di) {
|
foreach ($d as $key => $di) {
|
||||||
$sum -= $di;
|
$sum -= $di;
|
||||||
|
|
||||||
if ($sum <= 0) {
|
// The in array check is important to avoid duplicate cluster centers
|
||||||
$clusters[$i] = clone $points[$key];
|
if ($sum <= 0 && !\in_array($c = $points[$key], $clusters)) {
|
||||||
|
$clusters[$i] = clone $c;
|
||||||
|
$found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!$found) {
|
||||||
|
if (!\in_array($c = $points[\array_rand($points)], $clusters)) {
|
||||||
|
$clusters[$i] = clone $c;
|
||||||
|
$found = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($points as $point) {
|
foreach ($points as $point) {
|
||||||
$point->group = ($this->nearestClusterCenter($point, $clusters)[0]);
|
$point->group = $this->nearestClusterCenter($point, $clusters)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $clusters;
|
return $clusters;
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,187 @@ namespace phpOMS\Algorithm\Graph;
|
||||||
* @license OMS License 2.0
|
* @license OMS License 2.0
|
||||||
* @link https://jingga.app
|
* @link https://jingga.app
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*
|
|
||||||
* @todo Implement
|
|
||||||
*/
|
*/
|
||||||
final class MarkovChain
|
final class MarkovChain
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Order of the markov chain
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
private int $order = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trained data
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
private array $data = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param int $order Order of the markov chain
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function __construct(int $order = 1)
|
||||||
|
{
|
||||||
|
$this->order = $order;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create markov chain based on input
|
||||||
|
*
|
||||||
|
* @param array $values Training values
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function train(array $values) : void
|
||||||
|
{
|
||||||
|
$temp = [];
|
||||||
|
$length = \count($values) - $this->order;
|
||||||
|
|
||||||
|
$unique = \array_unique($values);
|
||||||
|
|
||||||
|
for ($i = 0; $i < $length; ++$i) {
|
||||||
|
$key = [];
|
||||||
|
for ($j = 0; $j < $this->order; ++$j) {
|
||||||
|
$key[] = $values[$i + $j];
|
||||||
|
}
|
||||||
|
|
||||||
|
$keyString = \implode(' ', $key);
|
||||||
|
|
||||||
|
if (!isset($temp[$keyString])) {
|
||||||
|
foreach ($unique as $value) {
|
||||||
|
$temp[$keyString][$value] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
++$temp[$keyString][$values[$i + 1]];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($temp as $key => $values) {
|
||||||
|
$sum = \array_sum($values);
|
||||||
|
foreach ($values as $idx => $value) {
|
||||||
|
$this->data[$key][$idx] = $value / $sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set training data
|
||||||
|
*
|
||||||
|
* @param array<array<int, int>> $values Training values
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function setTraining(array $values) : void
|
||||||
|
{
|
||||||
|
$this->data = $values;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a markov chain based on the training data.
|
||||||
|
*
|
||||||
|
* @param int $length Length of the markov chain
|
||||||
|
* @param array $start Start values of the markov chain
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function generate(int $length, array $start = null) : array
|
||||||
|
{
|
||||||
|
$orderKeys = \array_keys($this->data);
|
||||||
|
$orderValues = \array_keys(\reset($this->data));
|
||||||
|
|
||||||
|
$output = $start ?? \explode(' ', $orderKeys[\array_rand($orderKeys)]);
|
||||||
|
$key = $output;
|
||||||
|
|
||||||
|
for ($i = $this->order; $i < $length; ++$i) {
|
||||||
|
$keyString = \implode(' ', $key);
|
||||||
|
|
||||||
|
$prob = \mt_rand(1, 100) / 100;
|
||||||
|
$cProb = 0.0;
|
||||||
|
$val = null;
|
||||||
|
$new = null;
|
||||||
|
|
||||||
|
foreach (($this->data[$keyString] ?? []) as $val => $p) {
|
||||||
|
$cProb += $p;
|
||||||
|
|
||||||
|
if ($prob <= $cProb) {
|
||||||
|
$new = $val;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Couldn't find possible key
|
||||||
|
if ($new === null) {
|
||||||
|
$new = $orderValues[\array_rand($orderValues)];
|
||||||
|
}
|
||||||
|
|
||||||
|
$output[] = $new;
|
||||||
|
$key[] = $new;
|
||||||
|
|
||||||
|
\array_shift($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the probability for a certain markov chain.
|
||||||
|
*
|
||||||
|
* @param array $path Markov chain
|
||||||
|
*
|
||||||
|
* @return float
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function pathProbability(array $path) : float
|
||||||
|
{
|
||||||
|
$length = \count($path);
|
||||||
|
if ($length <= $this->order) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$key = \array_slice($path, 0, $this->order);
|
||||||
|
|
||||||
|
$prob = 1.0;
|
||||||
|
for ($i = $this->order; $i < $length; ++$i) {
|
||||||
|
$prob *= $this->data[\implode($key)][$path[$i]] ?? 0.0;
|
||||||
|
|
||||||
|
$key[] = $path[$i];
|
||||||
|
\array_shift($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $prob;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the probability for a certain state change in a markov chain
|
||||||
|
*
|
||||||
|
* @param array $state Current state of the markov chain
|
||||||
|
* @param mixed $next Next markov state
|
||||||
|
*
|
||||||
|
* @return float
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function stepProbability(array $state, mixed $next) : float
|
||||||
|
{
|
||||||
|
if (\count($state) !== $this->order) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->data[\implode(' ', $state)][$next] ?? 0.0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ class MazeGenerator
|
||||||
if (!empty($neighbors)) {
|
if (!empty($neighbors)) {
|
||||||
--$n;
|
--$n;
|
||||||
|
|
||||||
$next = $neighbors[\mt_rand(0, \count($neighbors) - 1)];
|
$next = $neighbors[\array_rand($neighbors, 1)];
|
||||||
$unvisited[$next[0] + 1][$next[1] + 1] = false;
|
$unvisited[$next[0] + 1][$next[1] + 1] = false;
|
||||||
|
|
||||||
if ($next[0] === $pos[0]) {
|
if ($next[0] === $pos[0]) {
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,6 @@ use phpOMS\Math\Solver\Root\Bisection;
|
||||||
* @see https://en.wikipedia.org/wiki/Glicko_rating_system
|
* @see https://en.wikipedia.org/wiki/Glicko_rating_system
|
||||||
* @see http://www.glicko.net/glicko/glicko2.pdf
|
* @see http://www.glicko.net/glicko/glicko2.pdf
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*
|
|
||||||
* @todo: implement
|
|
||||||
*/
|
*/
|
||||||
final class Glicko2
|
final class Glicko2
|
||||||
{
|
{
|
||||||
|
|
|
||||||
27
Business/BusinessHelper.php
Normal file
27
Business/BusinessHelper.php
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* PHP Version 8.1
|
||||||
|
*
|
||||||
|
* @package phpOMS\Business
|
||||||
|
* @copyright Dennis Eichhorn
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace phpOMS\Business;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Depreciation class.
|
||||||
|
*
|
||||||
|
* @package phpOMS\Business
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
final class BusinessHelper
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
@ -202,6 +202,8 @@ abstract class GrammarAbstract
|
||||||
$expression .= $element() . (\is_string($key) ? ' as ' . $key : '') . ', ';
|
$expression .= $element() . (\is_string($key) ? ' as ' . $key : '') . ', ';
|
||||||
} elseif ($element instanceof BuilderAbstract) {
|
} elseif ($element instanceof BuilderAbstract) {
|
||||||
$expression .= $element->toSql() . (\is_string($key) ? ' as ' . $key : '') . ', ';
|
$expression .= $element->toSql() . (\is_string($key) ? ' as ' . $key : '') . ', ';
|
||||||
|
} elseif (\is_int($element)) {
|
||||||
|
$expression .= $element . ', ';
|
||||||
} else {
|
} else {
|
||||||
throw new \InvalidArgumentException();
|
throw new \InvalidArgumentException();
|
||||||
}
|
}
|
||||||
|
|
@ -226,16 +228,19 @@ abstract class GrammarAbstract
|
||||||
$identifierStart = $this->systemIdentifierStart;
|
$identifierStart = $this->systemIdentifierStart;
|
||||||
$identifierEnd = $this->systemIdentifierEnd;
|
$identifierEnd = $this->systemIdentifierEnd;
|
||||||
|
|
||||||
if (\stripos($system, '(') !== false) {
|
// The order of this if/elseif statement is important!!!
|
||||||
|
if ($system === '*'
|
||||||
|
|| \stripos($system, '(') !== false
|
||||||
|
|| \is_numeric($system)
|
||||||
|
) {
|
||||||
$identifierStart = '';
|
$identifierStart = '';
|
||||||
$identifierEnd = '';
|
$identifierEnd = '';
|
||||||
}
|
} elseif ((\stripos($system, '.')) !== false) {
|
||||||
|
// This is actually slower than \explode(), despite knowing the first index
|
||||||
|
//$split = [\substr($system, 0, $pos), \substr($system, $pos + 1)];
|
||||||
|
|
||||||
// The following code could have been handled with \explode more elegantly but \explode needs more memory and more time
|
// Faster! But might requires more memory?
|
||||||
// Normally this wouldn't be a problem but in this case there are so many function calls to this routine,
|
$split = \explode('.', $system);
|
||||||
// that it makes sense to make this "minor" improvement.
|
|
||||||
if (($pos = \stripos($system, '.')) !== false) {
|
|
||||||
$split = [\substr($system, 0, $pos), \substr($system, $pos + 1)];
|
|
||||||
|
|
||||||
$identifierTwoStart = $identifierStart;
|
$identifierTwoStart = $identifierStart;
|
||||||
$identifierTwoEnd = $identifierEnd;
|
$identifierTwoEnd = $identifierEnd;
|
||||||
|
|
@ -250,11 +255,6 @@ abstract class GrammarAbstract
|
||||||
. $identifierTwoStart . $split[1] . $identifierTwoEnd;
|
. $identifierTwoStart . $split[1] . $identifierTwoEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($system === '*') {
|
|
||||||
$identifierStart = '';
|
|
||||||
$identifierEnd = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $identifierStart . $system . $identifierEnd;
|
return $identifierStart . $system . $identifierEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,23 @@ class DataMapperFactory
|
||||||
return $reader->get();
|
return $reader->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create read mapper
|
||||||
|
*
|
||||||
|
* @param ConnectionAbstract $db Database connection
|
||||||
|
*
|
||||||
|
* @return ReadMapper<T>
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public static function yield(ConnectionAbstract $db = null) : ReadMapper
|
||||||
|
{
|
||||||
|
/** @var ReadMapper<T> $reader */
|
||||||
|
$reader = new ReadMapper(new static(), $db ?? self::$db);
|
||||||
|
|
||||||
|
return $reader->yield();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create read mapper
|
* Create read mapper
|
||||||
*
|
*
|
||||||
|
|
@ -254,6 +271,48 @@ class DataMapperFactory
|
||||||
return (new ReadMapper(new static(), $db ?? self::$db))->count();
|
return (new ReadMapper(new static(), $db ?? self::$db))->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create read mapper
|
||||||
|
*
|
||||||
|
* @param ConnectionAbstract $db Database connection
|
||||||
|
*
|
||||||
|
* @return ReadMapper
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public static function sum(ConnectionAbstract $db = null) : ReadMapper
|
||||||
|
{
|
||||||
|
return (new ReadMapper(new static(), $db ?? self::$db))->sum();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create read mapper
|
||||||
|
*
|
||||||
|
* @param ConnectionAbstract $db Database connection
|
||||||
|
*
|
||||||
|
* @return ReadMapper
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public static function exists(ConnectionAbstract $db = null) : ReadMapper
|
||||||
|
{
|
||||||
|
return (new ReadMapper(new static(), $db ?? self::$db))->exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create read mapper
|
||||||
|
*
|
||||||
|
* @param ConnectionAbstract $db Database connection
|
||||||
|
*
|
||||||
|
* @return ReadMapper
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public static function has(ConnectionAbstract $db = null) : ReadMapper
|
||||||
|
{
|
||||||
|
return (new ReadMapper(new static(), $db ?? self::$db))->has();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create read mapper
|
* Create read mapper
|
||||||
*
|
*
|
||||||
|
|
@ -636,11 +695,14 @@ class DataMapperFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($count > $pageLimit) {
|
if ($count > $pageLimit) {
|
||||||
if (!$hasNext) { // @todo: can be maybe removed?
|
// @todo: can be maybe removed?
|
||||||
|
/*
|
||||||
|
if (!$hasNext) {
|
||||||
\array_pop($data);
|
\array_pop($data);
|
||||||
$hasNext = true;
|
$hasNext = true;
|
||||||
--$count;
|
--$count;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
if ($count > $pageLimit) {
|
if ($count > $pageLimit) {
|
||||||
$hasPrevious = true;
|
$hasPrevious = true;
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ abstract class MapperType extends Enum
|
||||||
{
|
{
|
||||||
public const GET = 1;
|
public const GET = 1;
|
||||||
|
|
||||||
|
public const GET_YIELD = 2;
|
||||||
|
|
||||||
public const GET_ALL = 4;
|
public const GET_ALL = 4;
|
||||||
|
|
||||||
public const FIND = 7;
|
public const FIND = 7;
|
||||||
|
|
@ -38,6 +40,12 @@ abstract class MapperType extends Enum
|
||||||
|
|
||||||
public const COUNT_MODELS = 12;
|
public const COUNT_MODELS = 12;
|
||||||
|
|
||||||
|
public const SUM_MODELS = 13;
|
||||||
|
|
||||||
|
public const MODEL_EXISTS = 14;
|
||||||
|
|
||||||
|
public const MODEL_HAS_RELATION = 15;
|
||||||
|
|
||||||
// -------------------------------------------- //
|
// -------------------------------------------- //
|
||||||
|
|
||||||
public const CREATE = 1001;
|
public const CREATE = 1001;
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,22 @@ final class ReadMapper extends DataMapperAbstract
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create yield mapper
|
||||||
|
*
|
||||||
|
* This makes execute() return a single object or an array of object depending the result size
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function yield() : self
|
||||||
|
{
|
||||||
|
$this->type = MapperType::GET_YIELD;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get raw result set
|
* Get raw result set
|
||||||
*
|
*
|
||||||
|
|
@ -102,6 +118,48 @@ final class ReadMapper extends DataMapperAbstract
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create sum mapper
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function sum() : self
|
||||||
|
{
|
||||||
|
$this->type = MapperType::SUM_MODELS;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create exists mapper
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function exists() : self
|
||||||
|
{
|
||||||
|
$this->type = MapperType::MODEL_EXISTS;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create has mapper
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function has() : self
|
||||||
|
{
|
||||||
|
$this->type = MapperType::MODEL_HAS_RELATION;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create random mapper
|
* Create random mapper
|
||||||
*
|
*
|
||||||
|
|
@ -148,6 +206,9 @@ final class ReadMapper extends DataMapperAbstract
|
||||||
case MapperType::GET:
|
case MapperType::GET:
|
||||||
/** @var null|Builder ...$options */
|
/** @var null|Builder ...$options */
|
||||||
return $this->executeGet(...$options);
|
return $this->executeGet(...$options);
|
||||||
|
case MapperType::GET_YIELD:
|
||||||
|
/** @var null|Builder ...$options */
|
||||||
|
return $this->executeGetYield(...$options);
|
||||||
case MapperType::GET_RAW:
|
case MapperType::GET_RAW:
|
||||||
/** @var null|Builder ...$options */
|
/** @var null|Builder ...$options */
|
||||||
return $this->executeGetRaw(...$options);
|
return $this->executeGetRaw(...$options);
|
||||||
|
|
@ -158,6 +219,12 @@ final class ReadMapper extends DataMapperAbstract
|
||||||
return $this->executeGetRaw();
|
return $this->executeGetRaw();
|
||||||
case MapperType::COUNT_MODELS:
|
case MapperType::COUNT_MODELS:
|
||||||
return $this->executeCount();
|
return $this->executeCount();
|
||||||
|
case MapperType::SUM_MODELS:
|
||||||
|
return $this->executeSum();
|
||||||
|
case MapperType::MODEL_EXISTS:
|
||||||
|
return $this->executeExists();
|
||||||
|
case MapperType::MODEL_HAS_RELATION:
|
||||||
|
return $this->executeHas();
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -188,13 +255,27 @@ final class ReadMapper extends DataMapperAbstract
|
||||||
$obj = [];
|
$obj = [];
|
||||||
|
|
||||||
// Get remaining objects (not available in memory cache) or remaining where clauses.
|
// Get remaining objects (not available in memory cache) or remaining where clauses.
|
||||||
$dbData = $this->executeGetRaw($query);
|
//$dbData = $this->executeGetRaw($query);
|
||||||
|
|
||||||
|
$ids = [];
|
||||||
|
foreach ($this->executeGetRawYield($query) as $row) {
|
||||||
|
if ($row === []) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($dbData as $row) {
|
|
||||||
$value = $row[$this->mapper::PRIMARYFIELD . '_d' . $this->depth];
|
$value = $row[$this->mapper::PRIMARYFIELD . '_d' . $this->depth];
|
||||||
$obj[$value] = $this->mapper::createBaseModel($row);
|
$obj[$value] = $this->mapper::createBaseModel($row);
|
||||||
|
|
||||||
$obj[$value] = $this->populateAbstract($row, $obj[$value]);
|
$obj[$value] = $this->populateAbstract($row, $obj[$value]);
|
||||||
|
|
||||||
|
$ids[] = $value;
|
||||||
|
|
||||||
|
// @todo: This is too slow, since it creates a query for every $row x relation type.
|
||||||
|
// Pulling it out would be nice.
|
||||||
|
// The problem with solving this is that in a many-to-many relationship a relation table is used
|
||||||
|
// BUT the relation data is not available in the object itself meaning after retrieving the object
|
||||||
|
// it cannot get assigned to the correct parent object.
|
||||||
|
// Other relation types are easy because either the parent or child object contain the relation info.
|
||||||
$this->loadHasManyRelations($obj[$value]);
|
$this->loadHasManyRelations($obj[$value]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -209,6 +290,34 @@ final class ReadMapper extends DataMapperAbstract
|
||||||
return $obj;
|
return $obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute mapper
|
||||||
|
*
|
||||||
|
* @param null|Builder $query Query to use instead of the internally generated query
|
||||||
|
* Careful, this doesn't merge with the internal query.
|
||||||
|
* If you want to merge it use ->query() instead
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function executeGetYield(Builder $query = null)
|
||||||
|
{
|
||||||
|
$primaryKeys = [];
|
||||||
|
$memberOfPrimaryField = $this->mapper::COLUMNS[$this->mapper::PRIMARYFIELD]['internal'];
|
||||||
|
|
||||||
|
if (isset($this->where[$memberOfPrimaryField])) {
|
||||||
|
$keys = $this->where[$memberOfPrimaryField][0]['value'];
|
||||||
|
$primaryKeys = \array_merge(\is_array($keys) ? $keys : [$keys], $primaryKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->executeGetRawYield($query) as $row) {
|
||||||
|
$obj = $this->mapper::createBaseModel($row);
|
||||||
|
$obj = $this->populateAbstract($row, $obj);
|
||||||
|
$this->loadHasManyRelations($obj);
|
||||||
|
|
||||||
|
yield $obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute mapper
|
* Execute mapper
|
||||||
*
|
*
|
||||||
|
|
@ -227,7 +336,7 @@ final class ReadMapper extends DataMapperAbstract
|
||||||
try {
|
try {
|
||||||
$results = false;
|
$results = false;
|
||||||
|
|
||||||
$sth = $this->db->con->prepare($a = $query->toSql());
|
$sth = $this->db->con->prepare($query->toSql());
|
||||||
if ($sth !== false) {
|
if ($sth !== false) {
|
||||||
$sth->execute();
|
$sth->execute();
|
||||||
$results = $sth->fetchAll(\PDO::FETCH_ASSOC);
|
$results = $sth->fetchAll(\PDO::FETCH_ASSOC);
|
||||||
|
|
@ -247,6 +356,47 @@ final class ReadMapper extends DataMapperAbstract
|
||||||
return $results === false ? [] : $results;
|
return $results === false ? [] : $results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute mapper
|
||||||
|
*
|
||||||
|
* @param null|Builder $query Query to use instead of the internally generated query
|
||||||
|
* Careful, this doesn't merge with the internal query.
|
||||||
|
* If you want to merge it use ->query() instead
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function executeGetRawYield(Builder $query = null)
|
||||||
|
{
|
||||||
|
$query ??= $this->getQuery();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$sth = $this->db->con->prepare($query->toSql());
|
||||||
|
if ($sth === false) {
|
||||||
|
yield [];
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sth->execute();
|
||||||
|
|
||||||
|
while ($row = $sth->fetch(\PDO::FETCH_ASSOC)) {
|
||||||
|
yield $row;
|
||||||
|
}
|
||||||
|
} catch (\Throwable $t) {
|
||||||
|
\phpOMS\Log\FileLogger::getInstance()->error(
|
||||||
|
\phpOMS\Log\FileLogger::MSG_FULL, [
|
||||||
|
'message' => $t->getMessage() . ':' . $query->toSql(),
|
||||||
|
'line' => __LINE__,
|
||||||
|
'file' => self::class,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
yield [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute mapper
|
* Execute mapper
|
||||||
*
|
*
|
||||||
|
|
@ -280,11 +430,65 @@ final class ReadMapper extends DataMapperAbstract
|
||||||
*/
|
*/
|
||||||
public function executeCount() : int
|
public function executeCount() : int
|
||||||
{
|
{
|
||||||
$query = $this->getQuery(null, ['COUNT(*)' => 'count']);
|
$query = $this->getQuery(
|
||||||
|
null,
|
||||||
|
[
|
||||||
|
'COUNT(' . (empty($this->columns) ? '*' : \implode($this->columns)) . ')' => 'count'
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
return (int) $query->execute()?->fetchColumn();
|
return (int) $query->execute()?->fetchColumn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sum the number of elements
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function executeSum() : int|float
|
||||||
|
{
|
||||||
|
$query = $this->getQuery(
|
||||||
|
null,
|
||||||
|
[
|
||||||
|
'SUM(' . (empty($this->columns) ? '*' : \implode($this->columns)) . ')' => 'sum'
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
return $query->execute()?->fetchColumn();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if any element exists
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function executeExists() : bool
|
||||||
|
{
|
||||||
|
$query = $this->getQuery(null, [1]);
|
||||||
|
|
||||||
|
return ($query->execute()?->fetchColumn() ?? 0) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if any element exists
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function executeHas() : bool
|
||||||
|
{
|
||||||
|
$obj = isset($this->where[$this->mapper::COLUMNS[$this->mapper::PRIMARYFIELD]['internal']])
|
||||||
|
? $this->mapper::createNullModel($this->where[$this->mapper::COLUMNS[$this->mapper::PRIMARYFIELD]['internal']][0]['value'])
|
||||||
|
: $this->columns([1])->executeGet();
|
||||||
|
|
||||||
|
return $this->hasManyRelations($obj);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get random object
|
* Get random object
|
||||||
*
|
*
|
||||||
|
|
@ -318,10 +522,18 @@ final class ReadMapper extends DataMapperAbstract
|
||||||
: $columns;
|
: $columns;
|
||||||
|
|
||||||
foreach ($columns as $key => $values) {
|
foreach ($columns as $key => $values) {
|
||||||
if (\is_string($values)) {
|
if (\is_string($values) || \is_int($values)) {
|
||||||
$query->selectAs($key, $values);
|
if (\is_int($key)) {
|
||||||
|
$query->select($values);
|
||||||
|
} else {
|
||||||
|
$query->selectAs($key, $values);
|
||||||
|
}
|
||||||
} elseif (($values['writeonly'] ?? false) === false || isset($this->with[$values['internal']])) {
|
} elseif (($values['writeonly'] ?? false) === false || isset($this->with[$values['internal']])) {
|
||||||
$query->selectAs($this->mapper::TABLE . '_d' . $this->depth . '.' . $key, $key . '_d' . $this->depth);
|
if (\is_int($key)) {
|
||||||
|
$query->select($key);
|
||||||
|
} else {
|
||||||
|
$query->selectAs($this->mapper::TABLE . '_d' . $this->depth . '.' . $key, $key . '_d' . $this->depth);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -442,7 +654,7 @@ final class ReadMapper extends DataMapperAbstract
|
||||||
$where1->where($this->mapper::TABLE . '_d' . $this->depth . '.' . $col, $comparison, $where['value'], 'and');
|
$where1->where($this->mapper::TABLE . '_d' . $this->depth . '.' . $col, $comparison, $where['value'], 'and');
|
||||||
|
|
||||||
$where2 = new Builder($this->db);
|
$where2 = new Builder($this->db);
|
||||||
$where2->select('1')
|
$where2->select('1') // @todo: why is this in quotes?
|
||||||
->from($this->mapper::TABLE . '_d' . $this->depth)
|
->from($this->mapper::TABLE . '_d' . $this->depth)
|
||||||
->where($this->mapper::TABLE . '_d' . $this->depth . '.' . $col, 'in', $alt);
|
->where($this->mapper::TABLE . '_d' . $this->depth . '.' . $col, 'in', $alt);
|
||||||
|
|
||||||
|
|
@ -961,4 +1173,87 @@ final class ReadMapper extends DataMapperAbstract
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if object has certain relations
|
||||||
|
*
|
||||||
|
* @param object $obj Object to check
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function hasManyRelations(object $obj) : bool
|
||||||
|
{
|
||||||
|
if (empty($this->with)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$primaryKey = $this->mapper::getObjectId($obj);
|
||||||
|
if (empty($primaryKey)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$refClass = null;
|
||||||
|
|
||||||
|
// @todo: check if there are more cases where the relation is already loaded with joins etc.
|
||||||
|
// there can be pseudo has many elements like localizations. They are has manies but these are already loaded with joins!
|
||||||
|
foreach ($this->with as $member => $withData) {
|
||||||
|
if (isset($this->mapper::HAS_MANY[$member])) {
|
||||||
|
$many = $this->mapper::HAS_MANY[$member];
|
||||||
|
if (isset($many['column'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo: withData doesn't store this directly, it is in [0]['private] ?!?!
|
||||||
|
$isPrivate = $withData['private'] ?? false;
|
||||||
|
|
||||||
|
$objectMapper = $this->createRelationMapper($many['mapper']::exists(db: $this->db), $member);
|
||||||
|
if ($many['external'] === null/* same as $many['table'] !== $many['mapper']::TABLE */) {
|
||||||
|
$objectMapper->where($many['mapper']::COLUMNS[$many['self']]['internal'], $primaryKey);
|
||||||
|
} else {
|
||||||
|
$query = new Builder($this->db, true);
|
||||||
|
$query->leftJoin($many['table'])
|
||||||
|
->on($many['mapper']::TABLE . '_d1.' . $many['mapper']::PRIMARYFIELD, '=', $many['table'] . '.' . $many['external'])
|
||||||
|
->where($many['table'] . '.' . $many['self'], '=', $primaryKey);
|
||||||
|
|
||||||
|
// Cannot use join, because join only works on members and we don't have members for a relation table
|
||||||
|
// This is why we need to create a "base" query which contians the join on table columns
|
||||||
|
$objectMapper->query($query);
|
||||||
|
}
|
||||||
|
|
||||||
|
$objects = $objectMapper->execute();
|
||||||
|
if (empty($objects) || $objects === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} elseif (isset($this->mapper::OWNS_ONE[$member])
|
||||||
|
|| isset($this->mapper::BELONGS_TO[$member])
|
||||||
|
) {
|
||||||
|
$relation = isset($this->mapper::OWNS_ONE[$member])
|
||||||
|
? $this->mapper::OWNS_ONE[$member]
|
||||||
|
: $this->mapper::BELONGS_TO[$member];
|
||||||
|
|
||||||
|
if (\count($withData) < 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var ReadMapper $relMapper */
|
||||||
|
$relMapper = $this->createRelationMapper($relation['mapper']::reader($this->db), $member);
|
||||||
|
|
||||||
|
$isPrivate = $withData['private'] ?? false;
|
||||||
|
if ($isPrivate) {
|
||||||
|
if ($refClass === null) {
|
||||||
|
$refClass = new \ReflectionClass($obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
$refProp = $refClass->getProperty($member);
|
||||||
|
return $relMapper->hasManyRelations($refProp->getValue($obj));
|
||||||
|
} else {
|
||||||
|
return $relMapper->hasManyRelations($obj->{$member});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -232,7 +232,7 @@ final class WriteMapper extends DataMapperAbstract
|
||||||
if (isset($this->mapper::BELONGS_TO[$propertyName]['by'])) {
|
if (isset($this->mapper::BELONGS_TO[$propertyName]['by'])) {
|
||||||
// has by (obj is stored as a different model e.g. model = profile but reference/db is account)
|
// has by (obj is stored as a different model e.g. model = profile but reference/db is account)
|
||||||
|
|
||||||
if ($this->mapper::BELONGS_TO[$propertyName]['private']) {
|
if ($this->mapper::BELONGS_TO[$propertyName]['private'] ?? false) {
|
||||||
$refClass = new \ReflectionClass($obj);
|
$refClass = new \ReflectionClass($obj);
|
||||||
$refProp = $refClass->getProperty($this->mapper::BELONGS_TO[$propertyName]['by']);
|
$refProp = $refClass->getProperty($this->mapper::BELONGS_TO[$propertyName]['by']);
|
||||||
$obj = $refProp->getValue($obj);
|
$obj = $refProp->getValue($obj);
|
||||||
|
|
@ -339,9 +339,10 @@ final class WriteMapper extends DataMapperAbstract
|
||||||
$relProperty = $relReflectionClass->getProperty($internalName);
|
$relProperty = $relReflectionClass->getProperty($internalName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo maybe consider to just set the column type to object, and then check for that (might be faster)
|
// @todo maybe consider to just set the column type to object, and then check for that (might be faster)
|
||||||
if (isset($mapper::BELONGS_TO[$internalName])
|
if (isset($mapper::BELONGS_TO[$internalName])
|
||||||
|| isset($mapper::OWNS_ONE[$internalName])) {
|
|| isset($mapper::OWNS_ONE[$internalName])
|
||||||
|
) {
|
||||||
if ($isRelPrivate) {
|
if ($isRelPrivate) {
|
||||||
$relProperty->setValue($value, $this->mapper::createNullModel($objId));
|
$relProperty->setValue($value, $this->mapper::createNullModel($objId));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -280,11 +280,7 @@ class Builder extends BuilderAbstract
|
||||||
/** @var mixed[] $columns */
|
/** @var mixed[] $columns */
|
||||||
/** @var mixed $column */
|
/** @var mixed $column */
|
||||||
foreach ($columns as $column) {
|
foreach ($columns as $column) {
|
||||||
if (\is_string($column) || $column instanceof self) {
|
$this->selects[] = $column;
|
||||||
$this->selects[] = $column;
|
|
||||||
} else {
|
|
||||||
throw new \InvalidArgumentException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,6 @@ use phpOMS\DataStorage\Database\Query\Where;
|
||||||
* @license OMS License 2.0
|
* @license OMS License 2.0
|
||||||
* @link https://jingga.app
|
* @link https://jingga.app
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*
|
|
||||||
* @todo Karaka/phpOMS#33
|
|
||||||
* Implement missing grammar & builder functions
|
|
||||||
* Missing elements are e.g. sum, merge etc.
|
|
||||||
*/
|
*/
|
||||||
class Grammar extends GrammarAbstract
|
class Grammar extends GrammarAbstract
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ class City
|
||||||
* @var string
|
* @var string
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected string $countryCode = '';
|
public string $countryCode = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* State code.
|
* State code.
|
||||||
|
|
@ -46,7 +46,7 @@ class City
|
||||||
* @var string
|
* @var string
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected string $state = '';
|
public string $state = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* City name.
|
* City name.
|
||||||
|
|
@ -54,7 +54,7 @@ class City
|
||||||
* @var string
|
* @var string
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected string $name = '';
|
public string $name = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Postal code.
|
* Postal code.
|
||||||
|
|
@ -62,7 +62,7 @@ class City
|
||||||
* @var int
|
* @var int
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected int $postal = 0;
|
public int $postal = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Latitude.
|
* Latitude.
|
||||||
|
|
@ -70,7 +70,7 @@ class City
|
||||||
* @var float
|
* @var float
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected float $lat = 0.0;
|
public float $lat = 0.0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Longitude.
|
* Longitude.
|
||||||
|
|
@ -78,7 +78,7 @@ class City
|
||||||
* @var float
|
* @var float
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected float $long = 0.0;
|
public float $long = 0.0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get city name
|
* Get city name
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ class Country
|
||||||
* @var string
|
* @var string
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected string $name = '';
|
public string $name = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Country code.
|
* Country code.
|
||||||
|
|
@ -46,7 +46,7 @@ class Country
|
||||||
* @var string
|
* @var string
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected string $code2 = '';
|
public string $code2 = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Country code.
|
* Country code.
|
||||||
|
|
@ -54,7 +54,7 @@ class Country
|
||||||
* @var string
|
* @var string
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected string $code3 = '';
|
public string $code3 = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Country code.
|
* Country code.
|
||||||
|
|
@ -62,7 +62,7 @@ class Country
|
||||||
* @var int
|
* @var int
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected int $numeric = 0;
|
public int $numeric = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Country subdevision.
|
* Country subdevision.
|
||||||
|
|
@ -70,7 +70,7 @@ class Country
|
||||||
* @var string
|
* @var string
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected string $subdevision = '';
|
public string $subdevision = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Country developed.
|
* Country developed.
|
||||||
|
|
@ -78,7 +78,7 @@ class Country
|
||||||
* @var bool
|
* @var bool
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected bool $isDeveloped = false;
|
public bool $isDeveloped = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get id
|
* Get id
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ class Currency
|
||||||
* @var string
|
* @var string
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected string $name = '';
|
public string $name = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Currency code.
|
* Currency code.
|
||||||
|
|
@ -46,7 +46,7 @@ class Currency
|
||||||
* @var string
|
* @var string
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected string $code = '';
|
public string $code = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Currency symbol.
|
* Currency symbol.
|
||||||
|
|
@ -54,7 +54,7 @@ class Currency
|
||||||
* @var string
|
* @var string
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected string $symbol = '';
|
public string $symbol = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Currency number.
|
* Currency number.
|
||||||
|
|
@ -62,7 +62,7 @@ class Currency
|
||||||
* @var string
|
* @var string
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected string $number = '';
|
public string $number = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Currency subunits.
|
* Currency subunits.
|
||||||
|
|
@ -70,7 +70,7 @@ class Currency
|
||||||
* @var int
|
* @var int
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected int $subunits = 0;
|
public int $subunits = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Currency decimals.
|
* Currency decimals.
|
||||||
|
|
@ -78,7 +78,7 @@ class Currency
|
||||||
* @var string
|
* @var string
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected string $decimals = '';
|
public string $decimals = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Currency countries.
|
* Currency countries.
|
||||||
|
|
@ -86,7 +86,7 @@ class Currency
|
||||||
* @var string
|
* @var string
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected string $countries = '';
|
public string $countries = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get currency name
|
* Get currency name
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ class Iban
|
||||||
* @var string
|
* @var string
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected string $country = '';
|
public string $country = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iban chars.
|
* Iban chars.
|
||||||
|
|
@ -46,7 +46,7 @@ class Iban
|
||||||
* @var int
|
* @var int
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected int $chars = 2;
|
public int $chars = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iban bban.
|
* Iban bban.
|
||||||
|
|
@ -54,7 +54,7 @@ class Iban
|
||||||
* @var string
|
* @var string
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected string $bban = '';
|
public string $bban = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iban fields.
|
* Iban fields.
|
||||||
|
|
@ -62,7 +62,7 @@ class Iban
|
||||||
* @var string
|
* @var string
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected string $fields = '';
|
public string $fields = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get iban country
|
* Get iban country
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ class Language
|
||||||
* @var string
|
* @var string
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected string $name = '';
|
public string $name = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Language native.
|
* Language native.
|
||||||
|
|
@ -46,7 +46,7 @@ class Language
|
||||||
* @var string
|
* @var string
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected string $native = '';
|
public string $native = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Language code.
|
* Language code.
|
||||||
|
|
@ -54,7 +54,7 @@ class Language
|
||||||
* @var string
|
* @var string
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected string $code2 = '';
|
public string $code2 = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Language code.
|
* Language code.
|
||||||
|
|
@ -62,7 +62,7 @@ class Language
|
||||||
* @var string
|
* @var string
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected string $code3 = '';
|
public string $code3 = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Language code.
|
* Language code.
|
||||||
|
|
@ -70,7 +70,7 @@ class Language
|
||||||
* @var string
|
* @var string
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected string $code3Native = '';
|
public string $code3Native = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get id
|
* Get id
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ class ISO3166NameEnum extends Enum
|
||||||
|
|
||||||
public const _BTN = 'Bhutan';
|
public const _BTN = 'Bhutan';
|
||||||
|
|
||||||
public const _BOL = 'Bolivia (Plurinational State of)';
|
public const _BOL = 'Bolivia';
|
||||||
|
|
||||||
public const _BES = 'Bonaire, Sint Eustatius and Saba';
|
public const _BES = 'Bonaire, Sint Eustatius and Saba';
|
||||||
|
|
||||||
|
|
@ -234,7 +234,7 @@ class ISO3166NameEnum extends Enum
|
||||||
|
|
||||||
public const _IDN = 'Indonesia';
|
public const _IDN = 'Indonesia';
|
||||||
|
|
||||||
public const _IRN = 'Iran (Islamic Republic of)';
|
public const _IRN = 'Iran';
|
||||||
|
|
||||||
public const _IRQ = 'Iraq';
|
public const _IRQ = 'Iraq';
|
||||||
|
|
||||||
|
|
@ -260,9 +260,9 @@ class ISO3166NameEnum extends Enum
|
||||||
|
|
||||||
public const _KIR = 'Kiribati';
|
public const _KIR = 'Kiribati';
|
||||||
|
|
||||||
public const _PRK = 'Korea (Democratic People\'s Republic of)';
|
public const _PRK = 'North Korea';
|
||||||
|
|
||||||
public const _KOR = 'Korea (Republic of)';
|
public const _KOR = 'South Korea';
|
||||||
|
|
||||||
public const _KWT = 'Kuwait';
|
public const _KWT = 'Kuwait';
|
||||||
|
|
||||||
|
|
@ -314,9 +314,9 @@ class ISO3166NameEnum extends Enum
|
||||||
|
|
||||||
public const _MEX = 'Mexico';
|
public const _MEX = 'Mexico';
|
||||||
|
|
||||||
public const _FSM = 'Micronesia (Federated States of)';
|
public const _FSM = 'Micronesia';
|
||||||
|
|
||||||
public const _MDA = 'Moldova (Republic of)';
|
public const _MDA = 'Moldova';
|
||||||
|
|
||||||
public const _MCO = 'Monaco';
|
public const _MCO = 'Monaco';
|
||||||
|
|
||||||
|
|
@ -364,7 +364,7 @@ class ISO3166NameEnum extends Enum
|
||||||
|
|
||||||
public const _PLW = 'Palau';
|
public const _PLW = 'Palau';
|
||||||
|
|
||||||
public const _PSE = 'Palestine, State of';
|
public const _PSE = 'Palestine';
|
||||||
|
|
||||||
public const _PAN = 'Panama';
|
public const _PAN = 'Panama';
|
||||||
|
|
||||||
|
|
@ -460,11 +460,11 @@ class ISO3166NameEnum extends Enum
|
||||||
|
|
||||||
public const _SYR = 'Syrian Arab Republic';
|
public const _SYR = 'Syrian Arab Republic';
|
||||||
|
|
||||||
public const _TWN = 'Taiwan, Province of China[a]';
|
public const _TWN = 'Taiwan';
|
||||||
|
|
||||||
public const _TJK = 'Tajikistan';
|
public const _TJK = 'Tajikistan';
|
||||||
|
|
||||||
public const _TZA = 'Tanzania, United Republic of';
|
public const _TZA = 'Tanzania';
|
||||||
|
|
||||||
public const _THA = 'Thailand';
|
public const _THA = 'Thailand';
|
||||||
|
|
||||||
|
|
@ -494,7 +494,7 @@ class ISO3166NameEnum extends Enum
|
||||||
|
|
||||||
public const _ARE = 'United Arab Emirates';
|
public const _ARE = 'United Arab Emirates';
|
||||||
|
|
||||||
public const _GBR = 'United Kingdom of Great Britain and Northern Ireland';
|
public const _GBR = 'United Kingdom';
|
||||||
|
|
||||||
public const _USA = 'United States of America';
|
public const _USA = 'United States of America';
|
||||||
|
|
||||||
|
|
@ -506,7 +506,7 @@ class ISO3166NameEnum extends Enum
|
||||||
|
|
||||||
public const _VUT = 'Vanuatu';
|
public const _VUT = 'Vanuatu';
|
||||||
|
|
||||||
public const _VEN = 'Venezuela (Bolivarian Republic of)';
|
public const _VEN = 'Venezuela';
|
||||||
|
|
||||||
public const _VNM = 'Viet Nam';
|
public const _VNM = 'Viet Nam';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,10 +37,44 @@ trait ISO3166Trait
|
||||||
{
|
{
|
||||||
/** @var string $code3 */
|
/** @var string $code3 */
|
||||||
$code3 = ISO3166TwoEnum::getName($code);
|
$code3 = ISO3166TwoEnum::getName($code);
|
||||||
|
if ($code3 === false) {
|
||||||
|
$code3 = '';
|
||||||
|
}
|
||||||
|
|
||||||
return self::getByName($code3);
|
return self::getByName($code3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get countries in a region
|
||||||
|
*
|
||||||
|
* @param string $region Region name
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public static function getSubregions(string $region) : array
|
||||||
|
{
|
||||||
|
$region = \strtolower($region);
|
||||||
|
|
||||||
|
switch ($region) {
|
||||||
|
case 'continents':
|
||||||
|
return ['Europe', 'Asia', 'America', 'Oceania', 'Africa'];
|
||||||
|
case 'europe':
|
||||||
|
return ['North-Europe', 'South-Europe', 'East-Europe', 'West-Europe'];
|
||||||
|
case 'asia':
|
||||||
|
return ['Central-Asia', 'South-Asia', 'Southeast-Asia', 'East-Asia', 'West-Asia'];
|
||||||
|
case 'america':
|
||||||
|
return ['North-america', 'South-america', 'Central-america', 'Caribbean'];
|
||||||
|
case 'oceania':
|
||||||
|
return ['Australia', 'Polynesia', 'Melanesia', 'Micronesia', 'Antarctica'];
|
||||||
|
case 'africa':
|
||||||
|
return ['North-Africa', 'South-Africa', 'East-Africa', 'West-Africa', 'Central-Africa'];
|
||||||
|
default:
|
||||||
|
return [$region];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get countries in a region
|
* Get countries in a region
|
||||||
*
|
*
|
||||||
|
|
@ -55,6 +89,14 @@ trait ISO3166Trait
|
||||||
$region = \strtolower($region);
|
$region = \strtolower($region);
|
||||||
|
|
||||||
switch ($region) {
|
switch ($region) {
|
||||||
|
case 'continents':
|
||||||
|
return \array_merge(
|
||||||
|
self::getRegion('europe'),
|
||||||
|
self::getRegion('asia'),
|
||||||
|
self::getRegion('america'),
|
||||||
|
self::getRegion('oceania'),
|
||||||
|
self::getRegion('africa')
|
||||||
|
);
|
||||||
case 'europe':
|
case 'europe':
|
||||||
return \array_merge(
|
return \array_merge(
|
||||||
self::getRegion('north-europe'),
|
self::getRegion('north-europe'),
|
||||||
|
|
@ -83,7 +125,7 @@ trait ISO3166Trait
|
||||||
self::getRegion('polynesia'),
|
self::getRegion('polynesia'),
|
||||||
self::getRegion('melanesia'),
|
self::getRegion('melanesia'),
|
||||||
self::getRegion('micronesia'),
|
self::getRegion('micronesia'),
|
||||||
self::getRegion('antartica')
|
self::getRegion('antarctica')
|
||||||
);
|
);
|
||||||
case 'africa':
|
case 'africa':
|
||||||
return \array_merge(
|
return \array_merge(
|
||||||
|
|
|
||||||
92
Localization/RegionEnum.php
Normal file
92
Localization/RegionEnum.php
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* PHP Version 8.1
|
||||||
|
*
|
||||||
|
* @package phpOMS\Localization
|
||||||
|
* @copyright Dennis Eichhorn
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace phpOMS\Localization;
|
||||||
|
|
||||||
|
use phpOMS\Stdlib\Base\Enum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Country codes ISO list.
|
||||||
|
*
|
||||||
|
* @package phpOMS\Localization
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
class RegionEnum extends Enum
|
||||||
|
{
|
||||||
|
public const EUROPE = 'Europe';
|
||||||
|
|
||||||
|
public const EU = 'EU';
|
||||||
|
|
||||||
|
public const EURO = 'Euro';
|
||||||
|
|
||||||
|
public const NORTH_EUROPE = 'North-Europe';
|
||||||
|
|
||||||
|
public const SOUTH_EUROPE = 'South-Europe';
|
||||||
|
|
||||||
|
public const EAST_EUROPE = 'East-Europe';
|
||||||
|
|
||||||
|
public const WEST_EUROPE = 'West-Europe';
|
||||||
|
|
||||||
|
public const AMERICA = 'America';
|
||||||
|
|
||||||
|
public const SOUTH_AMERICA = 'South-America';
|
||||||
|
|
||||||
|
public const NORTH_AMERICA = 'North-America';
|
||||||
|
|
||||||
|
public const CENTRAL_AMERICA = 'Central-America';
|
||||||
|
|
||||||
|
public const CARIBBEAN = 'Caribbean';
|
||||||
|
|
||||||
|
public const ASIA = 'Asia';
|
||||||
|
|
||||||
|
public const CENTRAL_ASIA = 'Central-Asia';
|
||||||
|
|
||||||
|
public const SOUTH_ASIA = 'South-Asia';
|
||||||
|
|
||||||
|
public const SOUTHEAST_ASIA = 'Southeast-Asia';
|
||||||
|
|
||||||
|
public const EAST_ASIA = 'East-Asia';
|
||||||
|
|
||||||
|
public const WEST_ASIA = 'West-Asia';
|
||||||
|
|
||||||
|
public const OCEANIA = 'Oceania';
|
||||||
|
|
||||||
|
public const AFRICA = 'Africa';
|
||||||
|
|
||||||
|
public const CENTRAL_AFRICA = 'Central-Africa';
|
||||||
|
|
||||||
|
public const SOUTH_AFRICA = 'South-Africa';
|
||||||
|
|
||||||
|
public const NORTH_AFRICA = 'North-Africa';
|
||||||
|
|
||||||
|
public const EAST_AFRICA = 'East-Africa';
|
||||||
|
|
||||||
|
public const WEST_AFRICA = 'West-Africa';
|
||||||
|
|
||||||
|
public const MIDDLE_EAST = 'Middle-East';
|
||||||
|
|
||||||
|
public const AUSTRALIA = 'Australia';
|
||||||
|
|
||||||
|
public const POLYNESIA = 'Polynesia';
|
||||||
|
|
||||||
|
public const MELANESIA = 'Melanesia';
|
||||||
|
|
||||||
|
public const MICRONESIA = 'Micronesia';
|
||||||
|
|
||||||
|
public const ANTARCTICA = 'Antarctica';
|
||||||
|
|
||||||
|
public const CONTINENTS = 'Continents';
|
||||||
|
}
|
||||||
121
Math/Geometry/ConvexHull/GrahamScan.php
Normal file
121
Math/Geometry/ConvexHull/GrahamScan.php
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* PHP Version 8.1
|
||||||
|
*
|
||||||
|
* @package phpOMS\Math\Geometry\ConvexHull
|
||||||
|
* @copyright Dennis Eichhorn
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace phpOMS\Math\Geometry\ConvexHull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Andrew's monotone chain convex hull algorithm class.
|
||||||
|
*
|
||||||
|
* @package phpOMS\Math\Geometry\ConvexHull
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
final class GrahamScan
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
private function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create convex hull
|
||||||
|
*
|
||||||
|
* @param array<int, array{x:int|float, y:int|float}> $points Points (Point Cloud)
|
||||||
|
*
|
||||||
|
* @return array<int, array{x:int|float, y:int|float}>
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public static function createConvexHull(array $points) : array
|
||||||
|
{
|
||||||
|
$count = \count($points);
|
||||||
|
|
||||||
|
if ($count < 3) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$min = 1;
|
||||||
|
$points = \array_merge([null], $points);
|
||||||
|
|
||||||
|
for ($i = 2; $i < $count; ++$i) {
|
||||||
|
if ($points[$i]['y'] < $points[$min]['y'] || ($points[$i]['y'] == $points[$min]['y'] && $points[$i]['x'] < $points[$min]['x'])) {
|
||||||
|
$min = $i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$temp = $points[1];
|
||||||
|
$points[1] = $points[$min];
|
||||||
|
$points[$min] = $temp;
|
||||||
|
|
||||||
|
$c = $points[1];
|
||||||
|
|
||||||
|
/** @var array<int, array{x:int|float, y:int|float}> $subpoints */
|
||||||
|
$subpoints = \array_slice($points, 2, $count);
|
||||||
|
\usort($subpoints, function (array $a, array $b) use ($c) : int {
|
||||||
|
// @todo: Might be wrong order of comparison
|
||||||
|
return \atan2($a['y'] - $c['y'], $a['x'] - $c['x']) <=> \atan2( $b['y'] - $c['y'], $b['x'] - $c['x']);
|
||||||
|
});
|
||||||
|
|
||||||
|
/** @var array<int, array{x:int|float, y:int|float}> $points */
|
||||||
|
$points = \array_merge([$points[0], $points[1]], $subpoints);
|
||||||
|
$points[0] = $points[$count];
|
||||||
|
|
||||||
|
$size = 1;
|
||||||
|
for ($i = 2; $i <= $count; ++$i) {
|
||||||
|
while (self::ccw($points[$size - 1], $points[$size], $points[$i]) <= 0) {
|
||||||
|
if ($size > 1) {
|
||||||
|
--$size;
|
||||||
|
} elseif ($i === $count) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
++$i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$temp = $points[$i];
|
||||||
|
$points[$size + 1] = $points[$i];
|
||||||
|
$points[$i] = $points[$size + 1];
|
||||||
|
++$size;
|
||||||
|
}
|
||||||
|
|
||||||
|
$hull = [];
|
||||||
|
for ($i = 1; $i <= $size; ++$i) {
|
||||||
|
$hull[] = $points[$i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $hull;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counterclockwise rotation
|
||||||
|
*
|
||||||
|
* @param array<x:int|float, y:int|float> $a Vector
|
||||||
|
* @param array<x:int|float, y:int|float> $b Vector
|
||||||
|
* @param array<x:int|float, y:int|float> $c Vector
|
||||||
|
*
|
||||||
|
* @return int|float
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public static function ccw(array $a, array $b, array $c) : int|float
|
||||||
|
{
|
||||||
|
return (($b['x'] - $a['x']) * ($c['y'] - $a['y']) - ($b['y'] - $a['y']) * ($c['x'] - $a['x']));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,200 +25,210 @@ namespace phpOMS\Math\Optimization;
|
||||||
*/
|
*/
|
||||||
class Simplex
|
class Simplex
|
||||||
{
|
{
|
||||||
private array $function = [];
|
private int $m = 0;
|
||||||
|
private int $n = 0;
|
||||||
|
|
||||||
private string $functionType = '';
|
private array $A = [];
|
||||||
|
|
||||||
private int|float $functionLimit = 0.0;
|
private array $b = [];
|
||||||
|
|
||||||
private array $constraints = [];
|
private array $c = [];
|
||||||
|
|
||||||
private array $constraintsType = [];
|
private int $v = 0;
|
||||||
|
|
||||||
private array $constraintsLimit = [];
|
private array $Basic = [];
|
||||||
|
|
||||||
private array $slackForm = [];
|
private array $Nonbasic = [];
|
||||||
|
|
||||||
private array $nonbasicSolution = [];
|
private function pivot (int $x, int $y)
|
||||||
|
|
||||||
private array $basicSolution = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define the function to optimize
|
|
||||||
*
|
|
||||||
* @param array $function Function to optimize
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
public function setFunction(array $function) : void
|
|
||||||
{
|
{
|
||||||
}
|
for ($j = 0; $j < $this->n; ++$j) {
|
||||||
|
if ($j !== $y) {
|
||||||
/**
|
$this->A[$x][$j] /= -$this->A[$x][$y];
|
||||||
* Add function constraint
|
|
||||||
*
|
|
||||||
* @param array $function Constraint function
|
|
||||||
* @param string $type Constraint type
|
|
||||||
* @param float $limit Constraint
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
public function addConstraint(array $function, string $type, float $limit) : void
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pivot element
|
|
||||||
*
|
|
||||||
* @param int $x X-Pivot
|
|
||||||
* @param int $y Y-Pivot
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
private function pivot(int $x, int $y) : void
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform simplex iteration
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
private function iterateSimplex() : void
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize simplex algorithm
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
private function initialize() : bool
|
|
||||||
{
|
|
||||||
$k = -1;
|
|
||||||
$minLimit = -1;
|
|
||||||
|
|
||||||
$m = \count($this->constraints);
|
|
||||||
$n = \count($this->function);
|
|
||||||
|
|
||||||
for ($i = 0; $i < $m; ++$i) {
|
|
||||||
if ($k === -1 || $this->constraintsLimit[$i] < $minLimit) {
|
|
||||||
$k = $i;
|
|
||||||
$minLimit = $this->constraintsLimit[$i];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->constraintsLimit[$k] >= 0) {
|
$this->b[$x] /= -$this->A[$x][$y];
|
||||||
for ($j = 0; $j < $n; ++$j) {
|
$this->A[$x][$y] = 1.0 / $this->A[$x][$y];
|
||||||
$this->nonbasicSolution[$j] = $j;
|
|
||||||
|
for ($i = 0; $i < $this->m; ++$i) {
|
||||||
|
if ($i !== $x) {
|
||||||
|
for ($j = 0; $j < $this->n; ++$j) {
|
||||||
|
if ($j !== $y) {
|
||||||
|
$this->A[$i][$j] += $this->A[$i][$y] * $this->A[$x][$j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->b[$i] += $this->A[$i][$y] / $this->b[$x];
|
||||||
|
$this->A[$i][$y] *= $this->A[$x][$y];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($j = 0; $j < $this->n; ++$j) {
|
||||||
|
if ($j !== $y) {
|
||||||
|
$this->c[$j] += $this->c[$y] * $this->A[$x][$j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->v += $this->c[$y] * $this->b[$x];
|
||||||
|
$this->c[$y] *= $this->A[$x][$y];
|
||||||
|
|
||||||
|
$temp = $this->Basic[$x];
|
||||||
|
$this->Basic[$x] = $this->Nonbasic[$y];
|
||||||
|
$this->Nonbasic[$y] = $temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function iterate() : int
|
||||||
|
{
|
||||||
|
$ind = -1;
|
||||||
|
$best = -1;
|
||||||
|
|
||||||
|
for ($j = 0; $j < $this->n; ++$j) {
|
||||||
|
if ($this->c[$j] > 0) {
|
||||||
|
if ($best === -1 || $this->Nonbasic[$j] < $ind) {
|
||||||
|
$ind = $this->Nonbasic[$j];
|
||||||
|
$best = $j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ind === -1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$maxConstraint = \INF;
|
||||||
|
$bestConstraint = -1;
|
||||||
|
|
||||||
|
for ($i = 0; $i < $this->m; ++$i) {
|
||||||
|
if ($this->A[$i][$best] < 0) {
|
||||||
|
$currentConstraint = -$this->b[$i] / $this->A[$i][$best];
|
||||||
|
if ($currentConstraint < $maxConstraint) {
|
||||||
|
$maxConstraint = $currentConstraint;
|
||||||
|
$bestConstraint = $i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($maxConstraint === \INF) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->pivot($bestConstraint, $best);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function initialize() : int
|
||||||
|
{
|
||||||
|
$k = -1;
|
||||||
|
$minB = -1;
|
||||||
|
|
||||||
|
for ($i = 0; $i < $this->m; ++$i) {
|
||||||
|
if ($k === -1 || $this->b[$i] < $minB) {
|
||||||
|
$k = $i;
|
||||||
|
$minB = $this->b[$i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->b[$k] >= 0) {
|
||||||
|
for ($j = 0; $j < $this->n; ++$j) {
|
||||||
|
$this->Nonbasic[$j] = $j;
|
||||||
}
|
}
|
||||||
|
|
||||||
for ($i = 0; $i < $m; ++$i) {
|
for ($i = 0; $i < $this->m; ++$i) {
|
||||||
$this->basicSolution[$i] = $n + $i;
|
$this->Basic[$i] = $this->n + $i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auxiliary LP
|
++$this->n;
|
||||||
++$n;
|
for ($j = 0; $j < $this->n; ++$j) {
|
||||||
for ($j = 0; $j < $n; ++$j) {
|
$this->Nonbasic[$j] = $j;
|
||||||
$this->nonbasicSolution[$j] = $j;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for ($i = 0; $i < $m; ++$i) {
|
for ($i = 0; $i < $this->m; ++$i) {
|
||||||
$this->basicSolution[$i] = $n + $i;
|
$this->Basic[$i] = $this->n + $i;
|
||||||
}
|
}
|
||||||
|
|
||||||
$oldFunction = $this->function;
|
$oldC = [];
|
||||||
$oldLimit = $this->functionLimit;
|
for ($j = 0; $j < $this->n - 1; ++$j) {
|
||||||
|
$oldC[$j] = $this->c[$j];
|
||||||
// Auxiliary function
|
|
||||||
$this->function[$n - 1] = -1;
|
|
||||||
$this->functionLimit = 0;
|
|
||||||
|
|
||||||
for ($j = 0; $j < $n - 1; ++$j) {
|
|
||||||
$this->function[$j] = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auxiliary constraints
|
$oldV = $this->v;
|
||||||
for ($i = 0; $i < $m; ++$i) {
|
|
||||||
$this->constraints[$i][$n - 1] = 1;
|
$this->c[$this->n - 1] = -1;
|
||||||
|
for ($j = 0; $j < $this->n - 1; ++$j) {
|
||||||
|
$this->c[$j] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->pivot($k, $n - 1);
|
$this->v = 0;
|
||||||
|
|
||||||
// Solve Auxiliary LP
|
for ($i = 0; $i < $this->m; ++$i) {
|
||||||
while ($this->iterateSimplex());
|
$this->A[$i][$this->n - 1] = 1;
|
||||||
|
|
||||||
if ($this->functionLimit !== 0) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$zBasic = -1;
|
$this->pivot($k, $this->n - 1);
|
||||||
for ($i = 0; $i < $m; ++$i) {
|
|
||||||
if ($this->basicSolution[$i] === $n - 1) {
|
while (!$this->iterate());
|
||||||
$zBasic = $i;
|
|
||||||
|
if ($this->v !== 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$basicZ = -1;
|
||||||
|
for ($i = 0; $i < $this->m; ++$i) {
|
||||||
|
if ($this->Basic[$i] === $this->n - 1) {
|
||||||
|
$basicZ = $i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($zBasic === -1) {
|
if ($basicZ !== -1) {
|
||||||
$this->pivot($zBasic, $n - 1);
|
$this->pivot($basicZ, $this->n - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
$zNonBasic = -1;
|
$nonbasicZ = -1;
|
||||||
for ($j = 0; $j < $n; ++$j) {
|
for ($j = 0; $j < $this->n; ++$j) {
|
||||||
if ($this->nonbasicSolution[$j] === $n - 1) {
|
if ($this->Nonbasic[$j] === $this->n - 1) {
|
||||||
$zNonBasic = $j;
|
$nonbasicZ = $j;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for ($i = 0; $i < $m; ++$i) {
|
for ($i = 0; $i < $this->m; ++$i) {
|
||||||
$this->constraints[$i][$zNonBasic] = $this->constraints[$i][$n - 1];
|
$this->A[$i][$nonbasicZ] = $this->A[$i][$this->n - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
$tmp = $this->nonbasicSolution[$n - 1];
|
$temp = $this->Nonbasic[$nonbasicZ];
|
||||||
$this->nonbasicSolution[$n - 1] = $this->nonbasicSolution[$zNonBasic];
|
$this->Nonbasic[$nonbasicZ] = $this->Nonbasic[$this->n - 1];
|
||||||
$this->nonbasicSolution[$zNonBasic] = $tmp;
|
$this->Nonbasic[$this->n - 1] = $temp;
|
||||||
|
|
||||||
--$n;
|
--$this->n;
|
||||||
|
for ($j = 0; $j < $this->n; ++$j) {
|
||||||
for ($j = 0; $j < $n; ++$j) {
|
if ($this->Nonbasic[$j] > $this->n) {
|
||||||
if ($this->nonbasicSolution[$j] > $n) {
|
--$this->Nonbasic[$j];
|
||||||
--$this->nonbasicSolution[$j];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for ($i = 0; $i < $m; ++$i) {
|
for ($i = 0; $i < $this->m; ++$i) {
|
||||||
if ($this->basicSolution[$i] > $n) {
|
if ($this->Basic[$i] > $this->n) {
|
||||||
--$this->basicSolution[$i];
|
--$this->Basic[$i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->functionLimit = $oldLimit;
|
for ($j = 0; $j < $this->n; ++$j) {
|
||||||
for ($j = 0; $j < $n; ++$j) {
|
$this->c[$j] = 0;
|
||||||
$this->function[$j] = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for ($j = 0; $j < $n; ++$j) {
|
$this->v = $oldV;
|
||||||
|
|
||||||
|
for ($j = 0; $j < $this->n; ++$j) {
|
||||||
$ok = false;
|
$ok = false;
|
||||||
|
for ($k = 0; $k < $this->n; ++$k) {
|
||||||
for ($jj = 0; $jj < $n; ++$jj) {
|
if ($j = $this->Nonbasic[$k]) {
|
||||||
if ($j === $this->nonbasicSolution[$jj]) {
|
$this->c[$k] += $oldC[$j];
|
||||||
$this->function[$jj] += $oldFunction[$j];
|
|
||||||
|
|
||||||
$ok = true;
|
$ok = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -228,32 +238,52 @@ class Simplex
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for ($i = 0; $i < $m; ++$i) {
|
for ($i = 0; $i < $this->m; ++$i) {
|
||||||
if ($j = $this->basicSolution[$i]) {
|
if ($j === $this->Basic[$i]) {
|
||||||
for ($jj = 0; $jj < $n; ++$jj) {
|
for ($k = 0; $k < $this->n; ++$k) {
|
||||||
$this->function[$jj] += $oldFunction[$j] * $this->constraints[$i][$jj];
|
$this->c[$k] = $oldC[$j] * $this->A[$i][$k];
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->functionLimit += $oldFunction[$j] * $this->constraintsLimit[$i];
|
$this->v += $oldC[$j] * $this->b[$i];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function solve(array $A, array $b, array $c)
|
||||||
* Solve the optimization
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
public function solve() : array
|
|
||||||
{
|
{
|
||||||
if (!$this->initialize()) {
|
$this->A = $A;
|
||||||
return [];
|
$this->b = $b;
|
||||||
|
$this->c = $c;
|
||||||
|
|
||||||
|
// @todo: createSlackForm() required?
|
||||||
|
|
||||||
|
$this->m = \count($A);
|
||||||
|
$this->n = \count(\reset($A));
|
||||||
|
|
||||||
|
if ($this->initialize() === -1) {
|
||||||
|
return [\array_fill(0, $this->m + $this->n, -2), \INF];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$code = 0;
|
||||||
|
while (!($code = $this->iterate()));
|
||||||
|
|
||||||
|
if ($code === -1) {
|
||||||
|
return [\array_fill(0, $this->m + $this->n, -1), \INF];
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
for ($j = 0; $j < $this->n; ++$j) {
|
||||||
|
$result[$this->Nonbasic[$j]] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($i = 0; $i < $this->m; ++$i) {
|
||||||
|
$result[$this->Basic[$i]] = $this->b[$i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$result, $this->v];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -344,241 +344,241 @@ final class HttpHeader extends HeaderAbstract
|
||||||
switch ($code) {
|
switch ($code) {
|
||||||
case RequestStatusCode::R_100:
|
case RequestStatusCode::R_100:
|
||||||
$this->set('', 'HTTP/1.0 100 Continue', true);
|
$this->set('', 'HTTP/1.0 100 Continue', true);
|
||||||
$this->set('Status', '100 Continue', true);
|
$this->set('Status', '100', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_101:
|
case RequestStatusCode::R_101:
|
||||||
$this->set('', 'HTTP/1.0 101 Switching protocols', true);
|
$this->set('', 'HTTP/1.0 101 Switching protocols', true);
|
||||||
$this->set('Status', '101 Switching protocols', true);
|
$this->set('Status', '101', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_102:
|
case RequestStatusCode::R_102:
|
||||||
$this->set('', 'HTTP/1.0 102 Processing', true);
|
$this->set('', 'HTTP/1.0 102 Processing', true);
|
||||||
$this->set('Status', '102 Processing', true);
|
$this->set('Status', '102', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_200:
|
case RequestStatusCode::R_200:
|
||||||
$this->set('', 'HTTP/1.0 200 OK', true);
|
$this->set('', 'HTTP/1.0 200 OK', true);
|
||||||
$this->set('Status', '200 OK', true);
|
$this->set('Status', '200', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_201:
|
case RequestStatusCode::R_201:
|
||||||
$this->set('', 'HTTP/1.0 201 Created', true);
|
$this->set('', 'HTTP/1.0 201 Created', true);
|
||||||
$this->set('Status', '201 Created', true);
|
$this->set('Status', '201', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_202:
|
case RequestStatusCode::R_202:
|
||||||
$this->set('', 'HTTP/1.0 202 Accepted', true);
|
$this->set('', 'HTTP/1.0 202 Accepted', true);
|
||||||
$this->set('Status', '202 Accepted', true);
|
$this->set('Status', '202', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_203:
|
case RequestStatusCode::R_203:
|
||||||
$this->set('', 'HTTP/1.0 203 Non-Authoritative Information', true);
|
$this->set('', 'HTTP/1.0 203 Non-Authoritative Information', true);
|
||||||
$this->set('Status', '203 Non-Authoritative Information', true);
|
$this->set('Status', '203', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_204:
|
case RequestStatusCode::R_204:
|
||||||
$this->set('', 'HTTP/1.0 204 No Content', true);
|
$this->set('', 'HTTP/1.0 204 No Content', true);
|
||||||
$this->set('Status', '204 No Content', true);
|
$this->set('Status', '204', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_205:
|
case RequestStatusCode::R_205:
|
||||||
$this->set('', 'HTTP/1.0 205 Reset Content', true);
|
$this->set('', 'HTTP/1.0 205 Reset Content', true);
|
||||||
$this->set('Status', '205 Reset Content', true);
|
$this->set('Status', '205', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_206:
|
case RequestStatusCode::R_206:
|
||||||
$this->set('', 'HTTP/1.0 206 Partial Content', true);
|
$this->set('', 'HTTP/1.0 206 Partial Content', true);
|
||||||
$this->set('Status', '206 Partial Content', true);
|
$this->set('Status', '206', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_207:
|
case RequestStatusCode::R_207:
|
||||||
$this->set('', 'HTTP/1.0 207 Multi-Status', true);
|
$this->set('', 'HTTP/1.0 207 Multi-Status', true);
|
||||||
$this->set('Status', '207 Multi-Status', true);
|
$this->set('Status', '207', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_300:
|
case RequestStatusCode::R_300:
|
||||||
$this->set('', 'HTTP/1.0 300 Multiple Choices', true);
|
$this->set('', 'HTTP/1.0 300 Multiple Choices', true);
|
||||||
$this->set('Status', '300 Multiple Choices', true);
|
$this->set('Status', '300', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_301:
|
case RequestStatusCode::R_301:
|
||||||
$this->set('', 'HTTP/1.0 301 Moved Permanently', true);
|
$this->set('', 'HTTP/1.0 301 Moved Permanently', true);
|
||||||
$this->set('Status', '301 Moved Permanently', true);
|
$this->set('Status', '301', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_302:
|
case RequestStatusCode::R_302:
|
||||||
$this->set('', 'HTTP/1.0 302 Found', true);
|
$this->set('', 'HTTP/1.0 302 Found', true);
|
||||||
$this->set('Status', '302 Found', true);
|
$this->set('Status', '302', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_303:
|
case RequestStatusCode::R_303:
|
||||||
$this->set('', 'HTTP/1.0 303 See Other', true);
|
$this->set('', 'HTTP/1.0 303 See Other', true);
|
||||||
$this->set('Status', '303 See Other', true);
|
$this->set('Status', '303', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_304:
|
case RequestStatusCode::R_304:
|
||||||
$this->set('', 'HTTP/1.0 304 Not Modified', true);
|
$this->set('', 'HTTP/1.0 304 Not Modified', true);
|
||||||
$this->set('Status', '304 Not Modified', true);
|
$this->set('Status', '304', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_305:
|
case RequestStatusCode::R_305:
|
||||||
$this->set('', 'HTTP/1.0 305 Use Proxy', true);
|
$this->set('', 'HTTP/1.0 305 Use Proxy', true);
|
||||||
$this->set('Status', '305 Use Proxy', true);
|
$this->set('Status', '305', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_306:
|
case RequestStatusCode::R_306:
|
||||||
$this->set('', 'HTTP/1.0 306 Switch Proxy', true);
|
$this->set('', 'HTTP/1.0 306 Switch Proxy', true);
|
||||||
$this->set('Status', '306 Switch Proxy', true);
|
$this->set('Status', '306', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_307:
|
case RequestStatusCode::R_307:
|
||||||
$this->set('', 'HTTP/1.0 307 Temporary Redirect', true);
|
$this->set('', 'HTTP/1.0 307 Temporary Redirect', true);
|
||||||
$this->set('Status', '307 Temporary Redirect', true);
|
$this->set('Status', '307', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_308:
|
case RequestStatusCode::R_308:
|
||||||
$this->set('', 'HTTP/1.0 308 Permanent Redirect', true);
|
$this->set('', 'HTTP/1.0 308 Permanent Redirect', true);
|
||||||
$this->set('Status', '308 Permanent Redirect', true);
|
$this->set('Status', '308', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_400:
|
case RequestStatusCode::R_400:
|
||||||
$this->set('', 'HTTP/1.0 400 Bad Request', true);
|
$this->set('', 'HTTP/1.0 400 Bad Request', true);
|
||||||
$this->set('Status', '400 Bad Request', true);
|
$this->set('Status', '400', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_401:
|
case RequestStatusCode::R_401:
|
||||||
$this->set('', 'HTTP/1.0 401 Unauthorized', true);
|
$this->set('', 'HTTP/1.0 401 Unauthorized', true);
|
||||||
$this->set('Status', '401 Unauthorized', true);
|
$this->set('Status', '401', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_402:
|
case RequestStatusCode::R_402:
|
||||||
$this->set('', 'HTTP/1.0 402 Payment Required', true);
|
$this->set('', 'HTTP/1.0 402 Payment Required', true);
|
||||||
$this->set('Status', '402 Payment Required', true);
|
$this->set('Status', '402', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_403:
|
case RequestStatusCode::R_403:
|
||||||
$this->set('', 'HTTP/1.0 403 Forbidden', true);
|
$this->set('', 'HTTP/1.0 403 Forbidden', true);
|
||||||
$this->set('Status', '403 Forbidden', true);
|
$this->set('Status', '403', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_404:
|
case RequestStatusCode::R_404:
|
||||||
$this->set('', 'HTTP/1.0 404 Not Found', true);
|
$this->set('', 'HTTP/1.0 404 Not Found', true);
|
||||||
$this->set('Status', '404 Not Found', true);
|
$this->set('Status', '404', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_405:
|
case RequestStatusCode::R_405:
|
||||||
$this->set('', 'HTTP/1.0 405 Method Not Allowed', true);
|
$this->set('', 'HTTP/1.0 405 Method Not Allowed', true);
|
||||||
$this->set('Status', '405 Method Not Allowed', true);
|
$this->set('Status', '405', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_406:
|
case RequestStatusCode::R_406:
|
||||||
$this->set('', 'HTTP/1.0 406 Not acceptable', true);
|
$this->set('', 'HTTP/1.0 406 Not acceptable', true);
|
||||||
$this->set('Status', '406 Not acceptable', true);
|
$this->set('Status', '406', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_407:
|
case RequestStatusCode::R_407:
|
||||||
$this->set('', 'HTTP/1.0 407 Proxy Authentication Required', true);
|
$this->set('', 'HTTP/1.0 407 Proxy Authentication Required', true);
|
||||||
$this->set('Status', '407 Proxy Authentication Required', true);
|
$this->set('Status', '407', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_408:
|
case RequestStatusCode::R_408:
|
||||||
$this->set('', 'HTTP/1.0 408 Request Timeout', true);
|
$this->set('', 'HTTP/1.0 408 Request Timeout', true);
|
||||||
$this->set('Status', '408 Request Timeout', true);
|
$this->set('Status', '408', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_409:
|
case RequestStatusCode::R_409:
|
||||||
$this->set('', 'HTTP/1.0 409 Conflict', true);
|
$this->set('', 'HTTP/1.0 409 Conflict', true);
|
||||||
$this->set('Status', '409 Conflict', true);
|
$this->set('Status', '409', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_410:
|
case RequestStatusCode::R_410:
|
||||||
$this->set('', 'HTTP/1.0 410 Gone', true);
|
$this->set('', 'HTTP/1.0 410 Gone', true);
|
||||||
$this->set('Status', '410 Gone', true);
|
$this->set('Status', '410', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_411:
|
case RequestStatusCode::R_411:
|
||||||
$this->set('', 'HTTP/1.0 411 Length Required', true);
|
$this->set('', 'HTTP/1.0 411 Length Required', true);
|
||||||
$this->set('Status', '411 Length Required', true);
|
$this->set('Status', '411', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_412:
|
case RequestStatusCode::R_412:
|
||||||
$this->set('', 'HTTP/1.0 412 Precondition Failed', true);
|
$this->set('', 'HTTP/1.0 412 Precondition Failed', true);
|
||||||
$this->set('Status', '412 Precondition Failed', true);
|
$this->set('Status', '412', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_413:
|
case RequestStatusCode::R_413:
|
||||||
$this->set('', 'HTTP/1.0 413 Request Entity Too Large', true);
|
$this->set('', 'HTTP/1.0 413 Request Entity Too Large', true);
|
||||||
$this->set('Status', '413 Request Entity Too Large', true);
|
$this->set('Status', '413', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_414:
|
case RequestStatusCode::R_414:
|
||||||
$this->set('', 'HTTP/1.0 414 Request-URI Too Long', true);
|
$this->set('', 'HTTP/1.0 414 Request-URI Too Long', true);
|
||||||
$this->set('Status', '414 Request-URI Too Long', true);
|
$this->set('Status', '414', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_415:
|
case RequestStatusCode::R_415:
|
||||||
$this->set('', 'HTTP/1.0 415 Unsupported Media Type', true);
|
$this->set('', 'HTTP/1.0 415 Unsupported Media Type', true);
|
||||||
$this->set('Status', '415 Unsupported Media Type', true);
|
$this->set('Status', '415', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_416:
|
case RequestStatusCode::R_416:
|
||||||
$this->set('', 'HTTP/1.0 416 Requested Range Not Satisfiable', true);
|
$this->set('', 'HTTP/1.0 416 Requested Range Not Satisfiable', true);
|
||||||
$this->set('Status', '416 Requested Range Not Satisfiable', true);
|
$this->set('Status', '416', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_417:
|
case RequestStatusCode::R_417:
|
||||||
$this->set('', 'HTTP/1.0 417 Expectation Failed', true);
|
$this->set('', 'HTTP/1.0 417 Expectation Failed', true);
|
||||||
$this->set('Status', '417 Expectation Failed', true);
|
$this->set('Status', '417', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_421:
|
case RequestStatusCode::R_421:
|
||||||
$this->set('', 'HTTP/1.0 421 Misdirected Request', true);
|
$this->set('', 'HTTP/1.0 421 Misdirected Request', true);
|
||||||
$this->set('Status', '421 Misdirected Request', true);
|
$this->set('Status', '421', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_422:
|
case RequestStatusCode::R_422:
|
||||||
$this->set('', 'HTTP/1.0 422 Unprocessable Entity', true);
|
$this->set('', 'HTTP/1.0 422 Unprocessable Entity', true);
|
||||||
$this->set('Status', '422 Unprocessable Entity', true);
|
$this->set('Status', '422', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_423:
|
case RequestStatusCode::R_423:
|
||||||
$this->set('', 'HTTP/1.0 423 Locked', true);
|
$this->set('', 'HTTP/1.0 423 Locked', true);
|
||||||
$this->set('Status', '423 Locked', true);
|
$this->set('Status', '423', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_424:
|
case RequestStatusCode::R_424:
|
||||||
$this->set('', 'HTTP/1.0 424 Failed Dependency', true);
|
$this->set('', 'HTTP/1.0 424 Failed Dependency', true);
|
||||||
$this->set('Status', '424 Failed Dependency', true);
|
$this->set('Status', '424', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_425:
|
case RequestStatusCode::R_425:
|
||||||
$this->set('', 'HTTP/1.0 425 Too Early', true);
|
$this->set('', 'HTTP/1.0 425 Too Early', true);
|
||||||
$this->set('Status', '425 Too Early', true);
|
$this->set('Status', '425', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_426:
|
case RequestStatusCode::R_426:
|
||||||
$this->set('', 'HTTP/1.0 426 Upgrade Required', true);
|
$this->set('', 'HTTP/1.0 426 Upgrade Required', true);
|
||||||
$this->set('Status', '426 Upgrade Required', true);
|
$this->set('Status', '426', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_428:
|
case RequestStatusCode::R_428:
|
||||||
$this->set('', 'HTTP/1.0 428 Precondition Required', true);
|
$this->set('', 'HTTP/1.0 428 Precondition Required', true);
|
||||||
$this->set('Status', '428 Precondition Required', true);
|
$this->set('Status', '428', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_429:
|
case RequestStatusCode::R_429:
|
||||||
$this->set('', 'HTTP/1.0 429 Too Many Requests', true);
|
$this->set('', 'HTTP/1.0 429 Too Many Requests', true);
|
||||||
$this->set('Status', '429 Too Many Requests', true);
|
$this->set('Status', '429', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_431:
|
case RequestStatusCode::R_431:
|
||||||
$this->set('', 'HTTP/1.0 431 Request Header Fields Too Large', true);
|
$this->set('', 'HTTP/1.0 431 Request Header Fields Too Large', true);
|
||||||
$this->set('Status', '431 Request Header Fields Too Large', true);
|
$this->set('Status', '431', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_451:
|
case RequestStatusCode::R_451:
|
||||||
$this->set('', 'HTTP/1.0 451 Unavailable For Legal Reasons', true);
|
$this->set('', 'HTTP/1.0 451 Unavailable For Legal Reasons', true);
|
||||||
$this->set('Status', '451 Unavailable For Legal Reasons', true);
|
$this->set('Status', '451', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_501:
|
case RequestStatusCode::R_501:
|
||||||
$this->set('', 'HTTP/1.0 501 Not Implemented', true);
|
$this->set('', 'HTTP/1.0 501 Not Implemented', true);
|
||||||
$this->set('Status', '501 Not Implemented', true);
|
$this->set('Status', '501', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_502:
|
case RequestStatusCode::R_502:
|
||||||
$this->set('', 'HTTP/1.0 502 Bad Gateway', true);
|
$this->set('', 'HTTP/1.0 502 Bad Gateway', true);
|
||||||
$this->set('Status', '502 Bad Gateway', true);
|
$this->set('Status', '502', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_503:
|
case RequestStatusCode::R_503:
|
||||||
$this->set('', 'HTTP/1.0 503 Service Temporarily Unavailable', true);
|
$this->set('', 'HTTP/1.0 503 Service Temporarily Unavailable', true);
|
||||||
$this->set('Status', '503 Service Temporarily Unavailable', true);
|
$this->set('Status', '503', true);
|
||||||
$this->set('Retry-After', 'Retry-After: 300', true);
|
$this->set('Retry-After', 'Retry-After: 300', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_504:
|
case RequestStatusCode::R_504:
|
||||||
$this->set('', 'HTTP/1.0 504 Gateway Timeout', true);
|
$this->set('', 'HTTP/1.0 504 Gateway Timeout', true);
|
||||||
$this->set('Status', '504 Gateway Timeout', true);
|
$this->set('Status', '504', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_505:
|
case RequestStatusCode::R_505:
|
||||||
$this->set('', 'HTTP/1.0 505 HTTP Version Not Supported', true);
|
$this->set('', 'HTTP/1.0 505 HTTP Version Not Supported', true);
|
||||||
$this->set('Status', '505 HTTP Version Not Supported', true);
|
$this->set('Status', '505', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_506:
|
case RequestStatusCode::R_506:
|
||||||
$this->set('', 'HTTP/1.0 506 HTTP Variant Also Negotiates', true);
|
$this->set('', 'HTTP/1.0 506 HTTP Variant Also Negotiates', true);
|
||||||
$this->set('Status', '506 HTTP Variant Also Negotiates', true);
|
$this->set('Status', '506', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_507:
|
case RequestStatusCode::R_507:
|
||||||
$this->set('', 'HTTP/1.0 507 Insufficient Storage', true);
|
$this->set('', 'HTTP/1.0 507 Insufficient Storage', true);
|
||||||
$this->set('Status', '507 Insufficient Storage', true);
|
$this->set('Status', '507', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_508:
|
case RequestStatusCode::R_508:
|
||||||
$this->set('', 'HTTP/1.0 508 Loop Detected', true);
|
$this->set('', 'HTTP/1.0 508 Loop Detected', true);
|
||||||
$this->set('Status', '508 Loop Detected', true);
|
$this->set('Status', '508', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_510:
|
case RequestStatusCode::R_510:
|
||||||
$this->set('', 'HTTP/1.0 510 Not Extended', true);
|
$this->set('', 'HTTP/1.0 510 Not Extended', true);
|
||||||
$this->set('Status', '510 Not Extended', true);
|
$this->set('Status', '510', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_511:
|
case RequestStatusCode::R_511:
|
||||||
$this->set('', 'HTTP/1.0 511 Network Authentication Required', true);
|
$this->set('', 'HTTP/1.0 511 Network Authentication Required', true);
|
||||||
$this->set('Status', '511 Network Authentication Required', true);
|
$this->set('Status', '511', true);
|
||||||
break;
|
break;
|
||||||
case RequestStatusCode::R_500:
|
case RequestStatusCode::R_500:
|
||||||
default:
|
default:
|
||||||
$this->set('', 'HTTP/1.0 500 Internal Server Error', true);
|
$this->set('', 'HTTP/1.0 500 Internal Server Error', true);
|
||||||
$this->set('Status', '500 Internal Server Error', true);
|
$this->set('Status', '500', true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -125,8 +125,7 @@ final class HttpResponse extends ResponseAbstract implements RenderableInterface
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var array{0:bool} $data */
|
return $this->getRaw(false);
|
||||||
return $this->getRaw($data[0] ?? false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ final class Rest
|
||||||
\curl_setopt($curl, \CURLOPT_POST, 1);
|
\curl_setopt($curl, \CURLOPT_POST, 1);
|
||||||
|
|
||||||
// handle different content types
|
// handle different content types
|
||||||
$contentType = $requestHeaders['content-type'] ?? [];
|
$contentType = $requestHeaders['Content-Type'] ?? [];
|
||||||
if (empty($contentType) || \in_array(MimeType::M_POST, $contentType)) {
|
if (empty($contentType) || \in_array(MimeType::M_POST, $contentType)) {
|
||||||
/* @phpstan-ignore-next-line */
|
/* @phpstan-ignore-next-line */
|
||||||
\curl_setopt($curl, \CURLOPT_POSTFIELDS, \http_build_query($request->data));
|
\curl_setopt($curl, \CURLOPT_POSTFIELDS, \http_build_query($request->data));
|
||||||
|
|
@ -94,7 +94,7 @@ final class Rest
|
||||||
|
|
||||||
// @todo: Replace boundary/ with the correct boundary= in the future.
|
// @todo: Replace boundary/ with the correct boundary= in the future.
|
||||||
// Currently this cannot be done due to a bug. If we do it now the server cannot correclty populate php://input
|
// Currently this cannot be done due to a bug. If we do it now the server cannot correclty populate php://input
|
||||||
$headers['content-type'] = 'Content-Type: multipart/form-data; boundary/' . $boundary;
|
$headers['Content-Type'] = 'Content-Type: multipart/form-data; boundary/' . $boundary;
|
||||||
$headers['content-length'] = 'Content-Length: ' . \strlen($data);
|
$headers['content-length'] = 'Content-Length: ' . \strlen($data);
|
||||||
|
|
||||||
\curl_setopt($curl, \CURLOPT_HTTPHEADER, $headers);
|
\curl_setopt($curl, \CURLOPT_HTTPHEADER, $headers);
|
||||||
|
|
@ -145,7 +145,7 @@ final class Rest
|
||||||
\curl_close($curl);
|
\curl_close($curl);
|
||||||
|
|
||||||
$raw = \substr(\is_bool($result) ? '' : $result, $len === false ? 0 : $len);
|
$raw = \substr(\is_bool($result) ? '' : $result, $len === false ? 0 : $len);
|
||||||
if (\stripos(\implode('', $response->header->get('content-type')), MimeType::M_JSON) !== false) {
|
if (\stripos(\implode('', $response->header->get('Content-Type')), MimeType::M_JSON) !== false) {
|
||||||
$temp = \json_decode($raw, true);
|
$temp = \json_decode($raw, true);
|
||||||
if (!\is_array($temp)) {
|
if (!\is_array($temp)) {
|
||||||
$temp = [];
|
$temp = [];
|
||||||
|
|
|
||||||
|
|
@ -2277,7 +2277,7 @@ class Email implements MessageInterface
|
||||||
'subject',
|
'subject',
|
||||||
'reply-to',
|
'reply-to',
|
||||||
'message-id',
|
'message-id',
|
||||||
'content-type',
|
'Content-Type',
|
||||||
'mime-version',
|
'mime-version',
|
||||||
'x-mailer',
|
'x-mailer',
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ abstract class ResponseAbstract implements \JsonSerializable, MessageInterface
|
||||||
*
|
*
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
public function get(mixed $key, string $type = null) : mixed
|
public function getData(mixed $key, string $type = null) : mixed
|
||||||
{
|
{
|
||||||
if ($key === null) {
|
if ($key === null) {
|
||||||
return $this->data;
|
return $this->data;
|
||||||
|
|
|
||||||
|
|
@ -545,7 +545,6 @@ abstract class ModuleAbstract
|
||||||
mixed $obj
|
mixed $obj
|
||||||
) : void
|
) : void
|
||||||
{
|
{
|
||||||
// @todo: consider to set different status code? (also for other createInvalid() functions)
|
|
||||||
$response->header->set('Content-Type', MimeType::M_JSON . '; charset=utf-8', true);
|
$response->header->set('Content-Type', MimeType::M_JSON . '; charset=utf-8', true);
|
||||||
$response->data[$request->uri->__toString()] = [
|
$response->data[$request->uri->__toString()] = [
|
||||||
'status' => NotificationLevel::WARNING,
|
'status' => NotificationLevel::WARNING,
|
||||||
|
|
@ -719,8 +718,6 @@ abstract class ModuleAbstract
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*
|
*
|
||||||
* @todo find a way to offload this to the cli in a different process (same for other similar functions)
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
protected function createModel(int $account, mixed $obj, string | \Closure $mapper, string $trigger, string $ip) : void
|
protected function createModel(int $account, mixed $obj, string | \Closure $mapper, string $trigger, string $ip) : void
|
||||||
|
|
|
||||||
|
|
@ -70,9 +70,8 @@ abstract class Enum
|
||||||
{
|
{
|
||||||
$reflect = new \ReflectionClass(static::class);
|
$reflect = new \ReflectionClass(static::class);
|
||||||
$constants = $reflect->getConstants();
|
$constants = $reflect->getConstants();
|
||||||
$keys = \array_keys($constants);
|
|
||||||
|
|
||||||
return $constants[$keys[\mt_rand(0, \count($constants) - 1)]];
|
return $constants[\array_rand($constants, 1)];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -119,8 +119,6 @@ abstract class EnumArray
|
||||||
*/
|
*/
|
||||||
public static function getRandom() : mixed
|
public static function getRandom() : mixed
|
||||||
{
|
{
|
||||||
$keys = \array_keys(static::$constants);
|
return static::$constants[\array_rand(static::$constants, 1)];
|
||||||
|
|
||||||
return static::$constants[$keys[\mt_rand(0, \count(static::$constants) - 1)]];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -158,17 +158,15 @@ class FloatInt implements SerializableInterface
|
||||||
*/
|
*/
|
||||||
public function getAmount(?int $decimals = 2) : string
|
public function getAmount(?int $decimals = 2) : string
|
||||||
{
|
{
|
||||||
$isNegative = $this->value < 0 ? 1 : 0;
|
|
||||||
|
|
||||||
$value = $this->value === 0
|
$value = $this->value === 0
|
||||||
? \str_repeat('0', self::MAX_DECIMALS)
|
? \str_repeat('0', self::MAX_DECIMALS)
|
||||||
: (string) \round($this->value, -self::MAX_DECIMALS + $decimals);
|
: (string) \round($this->value, -self::MAX_DECIMALS + $decimals);
|
||||||
|
|
||||||
$left = \substr($value, 0, -self::MAX_DECIMALS + $isNegative);
|
$left = \substr($value, 0, -self::MAX_DECIMALS);
|
||||||
|
|
||||||
/** @var string $left */
|
/** @var string $left */
|
||||||
$left = $left === false ? '0' : $left;
|
$left = $left === false ? '0' : $left;
|
||||||
$right = \substr($value, -self::MAX_DECIMALS + $isNegative);
|
$right = \substr($value, -self::MAX_DECIMALS);
|
||||||
|
|
||||||
if ($right === false) {
|
if ($right === false) {
|
||||||
throw new \Exception(); // @codeCoverageIgnore
|
throw new \Exception(); // @codeCoverageIgnore
|
||||||
|
|
|
||||||
|
|
@ -359,4 +359,90 @@ class SmartDateTime extends \DateTime
|
||||||
|
|
||||||
return $days;
|
return $days;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the start of the year based on a custom starting month
|
||||||
|
*
|
||||||
|
* @param int $month Start of the year (i.e. fiscal year)
|
||||||
|
*
|
||||||
|
* @return \DateTime
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public static function startOfYear(int $month = 1) : \DateTime
|
||||||
|
{
|
||||||
|
return new \DateTime(\date('Y') . '-' . \sprintf('%02d', $month) . '-01');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the end of the year based on a custom starting month
|
||||||
|
*
|
||||||
|
* @param int $month Start of the year (i.e. fiscal year)
|
||||||
|
*
|
||||||
|
* @return \DateTime
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public static function endOfYear(int $month = 1) : \DateTime
|
||||||
|
{
|
||||||
|
return new \DateTime(\date('Y') . '-' . self::calculateMonthIndex(13 - $month, $month) . '-31');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the start of the month
|
||||||
|
*
|
||||||
|
* @return \DateTime
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public static function startOfMonth() : \DateTime
|
||||||
|
{
|
||||||
|
return new \DateTime(\date('Y-m') . '-01');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the end of the month
|
||||||
|
*
|
||||||
|
* @return \DateTime
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public static function endOfMonth() : \DateTime
|
||||||
|
{
|
||||||
|
return new \DateTime(\date('Y-m-t'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the difference in months between two dates
|
||||||
|
*
|
||||||
|
* @param \DateTime $d1 First datetime
|
||||||
|
* @param \DateTime $d2 Second datetime
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public static function monthDiff(\DateTime $d1, \DateTime $d2) : int
|
||||||
|
{
|
||||||
|
$interval = $d1->diff($d2);
|
||||||
|
|
||||||
|
return ($interval->y * 12) + $interval->m;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the current month index based on the start of the fiscal year.
|
||||||
|
*
|
||||||
|
* @param int $month Current month
|
||||||
|
* @param int $start Start of the fiscal year (01 = January)
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*
|
||||||
|
* @since 1.0.0;
|
||||||
|
*/
|
||||||
|
public static function calculateMonthIndex(int $month, int $start = 1) : int
|
||||||
|
{
|
||||||
|
$mod = ($month - $start);
|
||||||
|
|
||||||
|
return \abs(($mod < 0 ? 12 + $mod : $mod) % 12) + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -250,9 +250,13 @@ class Node
|
||||||
foreach ($this->edges as $edge) {
|
foreach ($this->edges as $edge) {
|
||||||
$nodes = $edge->getNodes();
|
$nodes = $edge->getNodes();
|
||||||
|
|
||||||
$neighbors[] = $nodes[0] !== null && !$this->isEqual($nodes[0])
|
if ($edge->isDirected && isset($nodes[1]) && !$this->isEqual($nodes[1])) {
|
||||||
? $nodes[0]
|
$neighbors[] = $nodes[1];
|
||||||
: $nodes[1];
|
} else {
|
||||||
|
$neighbors[] = isset($nodes[0]) && !$this->isEqual($nodes[0])
|
||||||
|
? $nodes[0]
|
||||||
|
: $nodes[1];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $neighbors;
|
return $neighbors;
|
||||||
|
|
|
||||||
282
Stdlib/Tree/BinarySearchTree.php
Normal file
282
Stdlib/Tree/BinarySearchTree.php
Normal file
|
|
@ -0,0 +1,282 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* PHP Version 8.1
|
||||||
|
*
|
||||||
|
* @package phpOMS\Stdlib\Tree
|
||||||
|
* @copyright Dennis Eichhorn
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace phpOMS\Stdlib\Tree;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binary search tree.
|
||||||
|
*
|
||||||
|
* @package phpOMS\Stdlib\Tree
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
class BinarySearchTree
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Root node
|
||||||
|
*
|
||||||
|
* @var null|Node
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public ?Node $root = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param null|Node $root Root node
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function __construct(Node $root = null)
|
||||||
|
{
|
||||||
|
$this->root = $root;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search node by data
|
||||||
|
*
|
||||||
|
* @param mixed $data Data to search for
|
||||||
|
*
|
||||||
|
* @return null|Node
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function search(mixed $data) : ?Node
|
||||||
|
{
|
||||||
|
if ($this->root === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$comparison = $this->root->compare($data);
|
||||||
|
|
||||||
|
if ($comparison > 0) {
|
||||||
|
return $this->root->left?->search($data);
|
||||||
|
} elseif ($comparison < 0) {
|
||||||
|
return $this->root->right?->search($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->root;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the smallest node
|
||||||
|
*
|
||||||
|
* @return null|Node
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function minimum() : ?Node
|
||||||
|
{
|
||||||
|
if ($this->root === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->root->left === null) {
|
||||||
|
return $this->root;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->root->left->minimum();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the largest node
|
||||||
|
*
|
||||||
|
* @return null|Node
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function maximum() : ?Node
|
||||||
|
{
|
||||||
|
if ($this->root === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->root->right === null) {
|
||||||
|
return $this->root;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->root->right->minimum();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the predecessor of a node
|
||||||
|
*
|
||||||
|
* @param Node $node Node
|
||||||
|
*
|
||||||
|
* @return null|Node
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function predecessor(Node $node) : ?Node
|
||||||
|
{
|
||||||
|
if ($node->left !== null) {
|
||||||
|
return $node->left->maximum();
|
||||||
|
}
|
||||||
|
|
||||||
|
$top = $node->parent;
|
||||||
|
while ($top !== null && $top->compare($node->data)) {
|
||||||
|
$node = $top;
|
||||||
|
$top = $top->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $top;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the successor of a node
|
||||||
|
*
|
||||||
|
* @param Node $node Node
|
||||||
|
*
|
||||||
|
* @return null|Node
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function successor(Node $node) : ?Node
|
||||||
|
{
|
||||||
|
if ($node->right !== null) {
|
||||||
|
return $node->right->minimum();
|
||||||
|
}
|
||||||
|
|
||||||
|
$top = $node->parent;
|
||||||
|
while ($top !== null && $top->compare($node->data)) {
|
||||||
|
$node = $top;
|
||||||
|
$top = $top->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $top;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert a node
|
||||||
|
*
|
||||||
|
* @param Node $node Node
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function insert(Node $node) : void
|
||||||
|
{
|
||||||
|
if ($this->root === null) {
|
||||||
|
$new = new Node($node->key, $node->data);
|
||||||
|
$new->parent = null;
|
||||||
|
$new->tree = $this;
|
||||||
|
|
||||||
|
$this->root = $new;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$current = $this->root;
|
||||||
|
while (true) {
|
||||||
|
$comparison = $node->compare($current->data);
|
||||||
|
|
||||||
|
if ($comparison < 0) {
|
||||||
|
if ($current->left === null) {
|
||||||
|
$BST = new BinarySearchTree();
|
||||||
|
$new = new Node($node->key, $node->data);
|
||||||
|
$new->parent = $current;
|
||||||
|
$new->tree = $BST;
|
||||||
|
|
||||||
|
$BST->root = $new;
|
||||||
|
$current->left = $BST;
|
||||||
|
} else {
|
||||||
|
$current = $current->left->root;
|
||||||
|
}
|
||||||
|
} elseif ($comparison > 0) {
|
||||||
|
if ($current->right === null) {
|
||||||
|
$BST = new BinarySearchTree();
|
||||||
|
$new = new Node($node->key, $node->data);
|
||||||
|
$new->parent = $current;
|
||||||
|
$new->tree = $BST;
|
||||||
|
|
||||||
|
$BST->root = $new;
|
||||||
|
$current->right = $BST;
|
||||||
|
} else {
|
||||||
|
$current = $current->right->root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a node
|
||||||
|
*
|
||||||
|
* @param Node $node Node
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function delete(Node &$node) : void
|
||||||
|
{
|
||||||
|
if ($node->left === null && $node->right === null) {
|
||||||
|
if ($node->parent !== null) {
|
||||||
|
if ($node->parent->left !== null && $node->parent->left->root->compare($node->data) === 0) {
|
||||||
|
$node->parent->left = null;
|
||||||
|
} elseif ($node->parent->right !== null && $node->parent->right->root->compare($node) === 0) {
|
||||||
|
$node->parent->right = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$node = null;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$temp = null;
|
||||||
|
if ($node->left === null) {
|
||||||
|
$temp = $node->right->root;
|
||||||
|
if ($node->parent !== null) {
|
||||||
|
if ($node->parent->left !== null && $node->parent->left->root->compare($node->data) === 0) {
|
||||||
|
$node->parent->left = $temp->tree;
|
||||||
|
} elseif ($node->parent->right !== null && $node->parent->right->root->compare($node->data) === 0) {
|
||||||
|
$node->parent->right = $temp->tree;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$temp->parent = $node->parent;
|
||||||
|
|
||||||
|
$node = null;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($node->right === null) {
|
||||||
|
$temp = $node->left->root;
|
||||||
|
if ($node->parent !== null) {
|
||||||
|
if ($node->parent->left !== null && $node->parent->left->root->compare($node->data) === 0) {
|
||||||
|
$node->parent->left = $temp->tree;
|
||||||
|
} elseif ($node->parent->right !== null && $node->parent->right->root->compare($node->data) === 0) {
|
||||||
|
$node->parent->right = $temp->tree;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$temp->parent = $node->parent;
|
||||||
|
|
||||||
|
$node = null;
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
$temp = $this->successor($node);
|
||||||
|
$node->key = $temp->key;
|
||||||
|
$node->data = $temp->data;
|
||||||
|
|
||||||
|
$this->delete($temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
102
Stdlib/Tree/Node.php
Normal file
102
Stdlib/Tree/Node.php
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* PHP Version 8.1
|
||||||
|
*
|
||||||
|
* @package phpOMS\Stdlib\Tree
|
||||||
|
* @copyright Dennis Eichhorn
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace phpOMS\Stdlib\Tree;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tree node class.
|
||||||
|
*
|
||||||
|
* @package phpOMS\Stdlib\Tree
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
class Node
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Key of the node
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public string $key = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data of the node
|
||||||
|
*
|
||||||
|
* @var mixed
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public mixed $data = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sub-tree to the left
|
||||||
|
*
|
||||||
|
* @var null|BinarySearchTree
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public ?BinarySearchTree $left = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sub-tree to the right
|
||||||
|
*
|
||||||
|
* @var null|BinarySearchTree
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public ?BinarySearchTree $right = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parent node
|
||||||
|
*
|
||||||
|
* @var null|Node
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public ?self $parent = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parent tree
|
||||||
|
*
|
||||||
|
* @var null|BinarySearchTree
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public ?BinarySearchTree $tree = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param string $key Node key
|
||||||
|
* @param mixed $data Node data
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function __construct(string $key, mixed $data = null)
|
||||||
|
{
|
||||||
|
$this->key = $key;
|
||||||
|
$this->data = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare node data
|
||||||
|
*
|
||||||
|
* @param mixed $data Node data to compare with
|
||||||
|
*
|
||||||
|
* @return -1|0|1
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public function compare(mixed $data) : int
|
||||||
|
{
|
||||||
|
return $this->data <=> $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -458,8 +458,6 @@ abstract class MimeType extends Enum
|
||||||
|
|
||||||
public const M_EVY = 'application/x-envoy';
|
public const M_EVY = 'application/x-envoy';
|
||||||
|
|
||||||
public const M_EXE = 'application/x-msdownload';
|
|
||||||
|
|
||||||
public const M_EXI = 'application/exi';
|
public const M_EXI = 'application/exi';
|
||||||
|
|
||||||
public const M_EXT = 'application/vnd.novadigm.ext';
|
public const M_EXT = 'application/vnd.novadigm.ext';
|
||||||
|
|
@ -2010,6 +2008,20 @@ abstract class MimeType extends Enum
|
||||||
|
|
||||||
public const M_123 = 'application/vnd.lotus-1-2-3';
|
public const M_123 = 'application/vnd.lotus-1-2-3';
|
||||||
|
|
||||||
|
public const M_PEXE = 'vnd.microsoft.portable-executable';
|
||||||
|
|
||||||
|
public const M_EXE = 'application/exe';
|
||||||
|
|
||||||
|
public const M_DEXE = 'application/dos-exe';
|
||||||
|
|
||||||
|
public const M_XEXE = 'application/x-winexe';
|
||||||
|
|
||||||
|
public const M_MDEXE = 'application/msdos-windows';
|
||||||
|
|
||||||
|
public const M_MSP = 'application/x-msdos-program';
|
||||||
|
|
||||||
|
public const M_XMDEXE = 'application/x-msdownload';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get mime from file extension
|
* Get mime from file extension
|
||||||
*
|
*
|
||||||
|
|
@ -2036,7 +2048,6 @@ abstract class MimeType extends Enum
|
||||||
* @return null|string
|
* @return null|string
|
||||||
*
|
*
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
* @todo continue implementation
|
|
||||||
*/
|
*/
|
||||||
public static function mimeToExtension(string $mime) : ?string
|
public static function mimeToExtension(string $mime) : ?string
|
||||||
{
|
{
|
||||||
|
|
@ -2046,6 +2057,10 @@ abstract class MimeType extends Enum
|
||||||
case self::M_JPEG:
|
case self::M_JPEG:
|
||||||
case self::M_JPG:
|
case self::M_JPG:
|
||||||
return 'jpg';
|
return 'jpg';
|
||||||
|
case self::M_PNG:
|
||||||
|
return 'png';
|
||||||
|
case self::M_SVG:
|
||||||
|
return 'svg';
|
||||||
case self::M_BMP:
|
case self::M_BMP:
|
||||||
return 'bmp';
|
return 'bmp';
|
||||||
case self::M_GIF:
|
case self::M_GIF:
|
||||||
|
|
@ -2053,6 +2068,51 @@ abstract class MimeType extends Enum
|
||||||
case self::M_HTML:
|
case self::M_HTML:
|
||||||
case self::M_HTM:
|
case self::M_HTM:
|
||||||
return 'htm';
|
return 'htm';
|
||||||
|
case self::M_DOCX:
|
||||||
|
return 'docx';
|
||||||
|
case self::M_DOC:
|
||||||
|
return 'doc';
|
||||||
|
case self::M_ODT:
|
||||||
|
return 'odt';
|
||||||
|
case self::M_XLSX:
|
||||||
|
return 'xlsx';
|
||||||
|
case self::M_XLA:
|
||||||
|
case self::M_XLS:
|
||||||
|
return 'xls';
|
||||||
|
case self::M_ODS:
|
||||||
|
return 'ods';
|
||||||
|
case self::M_PPTX:
|
||||||
|
return 'pptx';
|
||||||
|
case self::M_PPT:
|
||||||
|
return 'ppt';
|
||||||
|
case self::M_ODP:
|
||||||
|
return 'odp';
|
||||||
|
case self::M_CSV:
|
||||||
|
return 'csv';
|
||||||
|
case self::M_XML:
|
||||||
|
return 'xml';
|
||||||
|
case self::M_JSON:
|
||||||
|
return 'json';
|
||||||
|
case self::M_ZIP:
|
||||||
|
return 'zip';
|
||||||
|
case self::M_7Z:
|
||||||
|
return '7z';
|
||||||
|
case self::M_RAR:
|
||||||
|
return 'rar';
|
||||||
|
case self::M_TAR:
|
||||||
|
return 'tar';
|
||||||
|
case self::M_MP3:
|
||||||
|
return 'mp3';
|
||||||
|
case self::M_MP4:
|
||||||
|
return 'mp4';
|
||||||
|
case self::M_PEXE:
|
||||||
|
case self::M_EXE:
|
||||||
|
case self::M_DEXE:
|
||||||
|
case self::M_XEXE:
|
||||||
|
case self::M_MDEXE:
|
||||||
|
case self::M_MSP:
|
||||||
|
case self::M_XMDEXE:
|
||||||
|
return 'exe';
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1091,7 +1091,7 @@ class QR extends TwoDAbstract
|
||||||
$howManuOut = 8 - (self::QR_FIND_FROM_RANDOM % 9);
|
$howManuOut = 8 - (self::QR_FIND_FROM_RANDOM % 9);
|
||||||
for ($i = 0; $i < $howManuOut; ++$i) {
|
for ($i = 0; $i < $howManuOut; ++$i) {
|
||||||
// @note: This is why the same content can result in different QR codes
|
// @note: This is why the same content can result in different QR codes
|
||||||
$remPos = \mt_rand(0, \count($checked_masks) - 1);
|
$remPos = \array_rand($checked_masks, 1);
|
||||||
unset($checked_masks[$remPos]);
|
unset($checked_masks[$remPos]);
|
||||||
$checked_masks = \array_values($checked_masks);
|
$checked_masks = \array_values($checked_masks);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -291,7 +291,7 @@ class Markdown
|
||||||
if (\strpos($Excerpt['text'], '>') !== false
|
if (\strpos($Excerpt['text'], '>') !== false
|
||||||
&& \preg_match("/^<((mailto:)?{$commonMarkEmail})>/i", $Excerpt['text'], $matches)
|
&& \preg_match("/^<((mailto:)?{$commonMarkEmail})>/i", $Excerpt['text'], $matches)
|
||||||
){
|
){
|
||||||
$url = $matches[1];
|
$url = UriFactory::build($matches[1]);
|
||||||
|
|
||||||
if (!isset($matches[2]))
|
if (!isset($matches[2]))
|
||||||
{
|
{
|
||||||
|
|
@ -496,7 +496,7 @@ class Markdown
|
||||||
if (\strpos($Excerpt['context'], 'http') !== false
|
if (\strpos($Excerpt['context'], 'http') !== false
|
||||||
&& \preg_match('/\bhttps?+:[\/]{2}[^\s<]+\b\/*+/ui', $Excerpt['context'], $matches, \PREG_OFFSET_CAPTURE)
|
&& \preg_match('/\bhttps?+:[\/]{2}[^\s<]+\b\/*+/ui', $Excerpt['context'], $matches, \PREG_OFFSET_CAPTURE)
|
||||||
) {
|
) {
|
||||||
$url = $matches[0][0];
|
$url = UriFactory::build($matches[0][0]);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'extent' => \strlen($matches[0][0]),
|
'extent' => \strlen($matches[0][0]),
|
||||||
|
|
@ -521,7 +521,7 @@ class Markdown
|
||||||
|
|
||||||
if (\strpos($Excerpt['text'], '>') !== false && \preg_match('/^<(\w++:\/{2}[^ >]++)>/i', $Excerpt['text'], $matches))
|
if (\strpos($Excerpt['text'], '>') !== false && \preg_match('/^<(\w++:\/{2}[^ >]++)>/i', $Excerpt['text'], $matches))
|
||||||
{
|
{
|
||||||
$url = $matches[1];
|
$url = UriFactory::build($matches[1]);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'extent' => \strlen($matches[0]),
|
'extent' => \strlen($matches[0]),
|
||||||
|
|
@ -3727,7 +3727,7 @@ class Markdown
|
||||||
|
|
||||||
if (\preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*+"|\'[^\']*+\'))?\s*+[)]/', $remainder, $matches))
|
if (\preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*+"|\'[^\']*+\'))?\s*+[)]/', $remainder, $matches))
|
||||||
{
|
{
|
||||||
$Element['attributes']['href'] = $matches[1];
|
$Element['attributes']['href'] = UriFactory::build($matches[1]);
|
||||||
|
|
||||||
if (isset($matches[2]))
|
if (isset($matches[2]))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,8 @@ class File
|
||||||
$source = self::$extensions;
|
$source = self::$extensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
$key = \mt_rand(0, \count($source) - 1);
|
$key = \array_rand($source, 1);
|
||||||
|
|
||||||
return $source[$key][\mt_rand(0, \count($source[$key]) - 1)];
|
return $source[$key][\array_rand($source[$key], 1)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -493,8 +493,8 @@ class Name
|
||||||
*/
|
*/
|
||||||
public static function generateName(array $type, string $origin = 'western') : string
|
public static function generateName(array $type, string $origin = 'western') : string
|
||||||
{
|
{
|
||||||
$rndType = \mt_rand(0, \count($type) - 1);
|
$rndType = \array_rand($type, 1);
|
||||||
|
|
||||||
return self::$names[$origin][$type[$rndType]][\mt_rand(0, \count(self::$names[$origin][$type[$rndType]]) - 1)];
|
return self::$names[$origin][$type[$rndType]][\array_rand(self::$names[$origin][$type[$rndType]], 1)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ class Phone
|
||||||
|
|
||||||
$numberString = \str_replace(
|
$numberString = \str_replace(
|
||||||
'$1',
|
'$1',
|
||||||
(string) $countries[\array_keys($countries)[\mt_rand(0, \count($countries) - 1)]],
|
(string) $countries[\array_rand($countries, 1)],
|
||||||
$numberString
|
$numberString
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ namespace phpOMS\tests\Algorithm\Clustering;
|
||||||
use phpOMS\Algorithm\Clustering\Kmeans;
|
use phpOMS\Algorithm\Clustering\Kmeans;
|
||||||
use phpOMS\Algorithm\Clustering\Point;
|
use phpOMS\Algorithm\Clustering\Point;
|
||||||
|
|
||||||
|
include_once __DIR__ . '/../../Autoloader.php';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @testdox phpOMS\tests\Algorithm\Clustering\KmeansTest: Clustering points/elements with the K-means algorithm
|
* @testdox phpOMS\tests\Algorithm\Clustering\KmeansTest: Clustering points/elements with the K-means algorithm
|
||||||
*
|
*
|
||||||
|
|
@ -34,6 +36,9 @@ final class KmeansTest extends \PHPUnit\Framework\TestCase
|
||||||
$seed = \mt_rand(\PHP_INT_MIN, \PHP_INT_MAX);
|
$seed = \mt_rand(\PHP_INT_MIN, \PHP_INT_MAX);
|
||||||
\mt_srand($seed);
|
\mt_srand($seed);
|
||||||
|
|
||||||
|
// The following seed + putting the loop to 1 would fail the test
|
||||||
|
//\mt_srand(1788576141);
|
||||||
|
|
||||||
$result = false;
|
$result = false;
|
||||||
|
|
||||||
// due to the random nature this can be false sometimes?!
|
// due to the random nature this can be false sometimes?!
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ final class HttpResponseTest extends \PHPUnit\Framework\TestCase
|
||||||
public function testResponseInputOutput() : void
|
public function testResponseInputOutput() : void
|
||||||
{
|
{
|
||||||
$this->response->setResponse(['a' => 1]);
|
$this->response->setResponse(['a' => 1]);
|
||||||
self::assertEquals(1, $this->response->get('a'));
|
self::assertEquals(1, $this->response->getData('a'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ final class RestTest extends \PHPUnit\Framework\TestCase
|
||||||
$request = new HttpRequest(new HttpUri('https://httpbin.org/post'));
|
$request = new HttpRequest(new HttpUri('https://httpbin.org/post'));
|
||||||
$request->setMethod(RequestMethod::POST);
|
$request->setMethod(RequestMethod::POST);
|
||||||
self::assertTrue($request->setData('pdata', 'abc'));
|
self::assertTrue($request->setData('pdata', 'abc'));
|
||||||
self::assertEquals('abc', REST::request($request)->get('form')['pdata'] ?? '');
|
self::assertEquals('abc', REST::request($request)->getData('form')['pdata'] ?? '');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -67,7 +67,7 @@ final class RestTest extends \PHPUnit\Framework\TestCase
|
||||||
$request = new HttpRequest(new HttpUri('https://httpbin.org/put'));
|
$request = new HttpRequest(new HttpUri('https://httpbin.org/put'));
|
||||||
$request->setMethod(RequestMethod::PUT);
|
$request->setMethod(RequestMethod::PUT);
|
||||||
self::assertTrue($request->setData('pdata', 'abc'));
|
self::assertTrue($request->setData('pdata', 'abc'));
|
||||||
self::assertEquals('abc', REST::request($request)->get('form')['pdata'] ?? '');
|
self::assertEquals('abc', REST::request($request)->getData('form')['pdata'] ?? '');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -80,7 +80,7 @@ final class RestTest extends \PHPUnit\Framework\TestCase
|
||||||
$request = new HttpRequest(new HttpUri('https://httpbin.org/delete'));
|
$request = new HttpRequest(new HttpUri('https://httpbin.org/delete'));
|
||||||
$request->setMethod(RequestMethod::DELETE);
|
$request->setMethod(RequestMethod::DELETE);
|
||||||
self::assertTrue($request->setData('ddata', 'abc'));
|
self::assertTrue($request->setData('ddata', 'abc'));
|
||||||
self::assertEquals('abc', REST::request($request)->get('form')['ddata'] ?? '');
|
self::assertEquals('abc', REST::request($request)->getData('form')['ddata'] ?? '');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -93,7 +93,7 @@ final class RestTest extends \PHPUnit\Framework\TestCase
|
||||||
$request = new HttpRequest(new HttpUri('https://httpbin.org/get'));
|
$request = new HttpRequest(new HttpUri('https://httpbin.org/get'));
|
||||||
$request->setMethod(RequestMethod::GET);
|
$request->setMethod(RequestMethod::GET);
|
||||||
self::assertTrue($request->setData('gdata', 'abc'));
|
self::assertTrue($request->setData('gdata', 'abc'));
|
||||||
self::assertEquals('abc', REST::request($request)->get('args')['gdata'] ?? '');
|
self::assertEquals('abc', REST::request($request)->getData('args')['gdata'] ?? '');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ final class ResponseAbstractTest extends \PHPUnit\Framework\TestCase
|
||||||
*/
|
*/
|
||||||
public function testDefault() : void
|
public function testDefault() : void
|
||||||
{
|
{
|
||||||
self::assertNull($this->response->get('asdf'));
|
self::assertNull($this->response->getData('asdf'));
|
||||||
self::assertEquals('', $this->response->getBody());
|
self::assertEquals('', $this->response->getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,6 +75,6 @@ final class ResponseAbstractTest extends \PHPUnit\Framework\TestCase
|
||||||
public function testDataInputOutput() : void
|
public function testDataInputOutput() : void
|
||||||
{
|
{
|
||||||
$this->response->set('asdf', false);
|
$this->response->set('asdf', false);
|
||||||
self::assertFalse($this->response->get('asdf'));
|
self::assertFalse($this->response->getData('asdf'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -258,7 +258,7 @@ final class ModuleAbstractTest extends \PHPUnit\Framework\TestCase
|
||||||
'message' => 'Test Message!',
|
'message' => 'Test Message!',
|
||||||
'response' => [1, 'test string', 'bool' => true],
|
'response' => [1, 'test string', 'bool' => true],
|
||||||
],
|
],
|
||||||
$response->get('')
|
$response->getData('')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -276,7 +276,7 @@ final class ModuleAbstractTest extends \PHPUnit\Framework\TestCase
|
||||||
|
|
||||||
self::assertEquals(
|
self::assertEquals(
|
||||||
[1, 'test string', 'bool' => true],
|
[1, 'test string', 'bool' => true],
|
||||||
$response->get('')
|
$response->getData('')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user