code fixes

This commit is contained in:
Dennis Eichhorn 2024-03-15 20:24:39 +00:00
parent 2a41801b77
commit 642b74ad22
50 changed files with 879 additions and 233 deletions

View File

@ -101,7 +101,7 @@ final class TesseractOcr
))); )));
if ($dpi < 300) { if ($dpi < 300) {
$split = \explode('.', $image); $split = \explode('.', $image);
$extension = \end($split); $extension = \end($split);
SystemUtils::runProc( SystemUtils::runProc(

View File

@ -116,8 +116,8 @@ final class AffinityPropagation implements ClusteringInterface
$n = \count($points); $n = \count($points);
$this->similarityMatrix = $this->createSimilarityMatrix($points); $this->similarityMatrix = $this->createSimilarityMatrix($points);
$this->responsibilityMatrix = clone $this->similarityMatrix; $this->responsibilityMatrix = $this->similarityMatrix;
$this->availabilityMatrix = clone $this->similarityMatrix; $this->availabilityMatrix = $this->similarityMatrix;
for ($c = 0; $c < $iterations; ++$c) { for ($c = 0; $c < $iterations; ++$c) {
for ($i = 0; $i < $n; ++$i) { for ($i = 0; $i < $n; ++$i) {
@ -188,8 +188,8 @@ final class AffinityPropagation implements ClusteringInterface
/** /**
* Find the nearest group for a point * Find the nearest group for a point
* *
* @param array<int, array<int, int|float> $similarityMatrix Similarity matrix * @param array<int, array<int, int|float>> $similarityMatrix Similarity matrix
* @param int $point Point id in the similarity matrix to compare * @param int $point Point id in the similarity matrix to compare
* *
* @return int * @return int
* *
@ -215,7 +215,7 @@ final class AffinityPropagation implements ClusteringInterface
*/ */
public function cluster(PointInterface $point) : ?PointInterface public function cluster(PointInterface $point) : ?PointInterface
{ {
$points = clone $this->points; $points = $this->points;
$points[] = $point; $points[] = $point;
$similarityMatrix = $this->createSimilarityMatrix($points); $similarityMatrix = $this->createSimilarityMatrix($points);

View File

@ -119,4 +119,36 @@ final class AgglomerativeClustering implements ClusteringInterface
return $distance / \count($setA) / \count($setB); return $distance / \count($setA) / \count($setB);
} }
/**
* {@inheritdoc}
*/
public function getCentroids() : array
{
return [];
}
/**
* {@inheritdoc}
*/
public function getClusters() : array
{
return [];
}
/**
* {@inheritdoc}
*/
public function cluster(PointInterface $point) : ?PointInterface
{
return null;
}
/**
* {@inheritdoc}
*/
public function getNoise() : array
{
return [];
}
} }

View File

@ -27,4 +27,35 @@ namespace phpOMS\Algorithm\Clustering;
*/ */
final class Birch implements ClusteringInterface final class Birch implements ClusteringInterface
{ {
/**
* {@inheritdoc}
*/
public function getCentroids() : array
{
return [];
}
/**
* {@inheritdoc}
*/
public function getClusters() : array
{
return [];
}
/**
* {@inheritdoc}
*/
public function cluster(PointInterface $point) : ?PointInterface
{
return null;
}
/**
* {@inheritdoc}
*/
public function getNoise() : array
{
return [];
}
} }

View File

@ -31,4 +31,35 @@ namespace phpOMS\Algorithm\Clustering;
*/ */
final class DivisiveClustering implements ClusteringInterface final class DivisiveClustering implements ClusteringInterface
{ {
/**
* {@inheritdoc}
*/
public function getCentroids() : array
{
return [];
}
/**
* {@inheritdoc}
*/
public function getClusters() : array
{
return [];
}
/**
* {@inheritdoc}
*/
public function cluster(PointInterface $point) : ?PointInterface
{
return null;
}
/**
* {@inheritdoc}
*/
public function getNoise() : array
{
return [];
}
} }

View File

@ -269,8 +269,8 @@ final class Kmeans implements ClusteringInterface
} }
foreach ($this->points as $point) { foreach ($this->points as $point) {
$c = $this->cluster($point); $c = $this->cluster($point);
$this->clusters[$c] = $point; $this->clusters[$c?->name] = $point;
} }
return $this->clusters; return $this->clusters;

View File

@ -27,4 +27,35 @@ namespace phpOMS\Algorithm\Clustering;
*/ */
final class SpectralClustering implements ClusteringInterface final class SpectralClustering implements ClusteringInterface
{ {
/**
* {@inheritdoc}
*/
public function getCentroids() : array
{
return [];
}
/**
* {@inheritdoc}
*/
public function getClusters() : array
{
return [];
}
/**
* {@inheritdoc}
*/
public function cluster(PointInterface $point) : ?PointInterface
{
return null;
}
/**
* {@inheritdoc}
*/
public function getNoise() : array
{
return [];
}
} }

View File

@ -0,0 +1,28 @@
<?php
/**
* Jingga
*
* PHP Version 8.1
*
* @package phpOMS\Scheduling\Dependency
* @copyright Dennis Eichhorn
* @license OMS License 2.0
* @version 1.0.0
* @link https://jingga.app
*/
declare(strict_types=1);
namespace phpOMS\Scheduling\Dependency;
/**
* Material.
*
* @package phpOMS\Scheduling\Dependency
* @license OMS License 2.0
* @link https://jingga.app
* @since 1.0.0
*/
class Material
{
public int $id = 0;
}

View File

@ -0,0 +1,28 @@
<?php
/**
* Jingga
*
* PHP Version 8.1
*
* @package phpOMS\Scheduling\Dependency
* @copyright Dennis Eichhorn
* @license OMS License 2.0
* @version 1.0.0
* @link https://jingga.app
*/
declare(strict_types=1);
namespace phpOMS\Scheduling\Dependency;
/**
* Material.
*
* @package phpOMS\Scheduling\Dependency
* @license OMS License 2.0
* @link https://jingga.app
* @since 1.0.0
*/
class Qualification
{
public int $id = 0;
}

View File

@ -24,32 +24,96 @@ namespace phpOMS\Scheduling;
*/ */
class Job class Job
{ {
/**
* Id
*
* @var int
* @since 1.0.0
*/
public int $id = 0; public int $id = 0;
/**
* Time of the execution
*
* @var int
* @since 1.0.0
*/
public int $executionTime = 0; public int $executionTime = 0;
/**
* Priority.
*
* @var float
* @since 1.0.0
*/
public float $priority = 0.0; public float $priority = 0.0;
/**
* Value this job generates.
*
* @var float
* @since 1.0.0
*/
public float $value = 0.0; public float $value = 0.0;
/**
* Cost of executing this job.
*
* @var float
* @since 1.0.0
*/
public float $cost = 0.0; public float $cost = 0.0;
/** How many iterations has this job been on hold in the queue */ /**
* How many iterations has this job been on hold in the queue.
*
* @var int
* @since 1.0.0
*/
public int $onhold = 0; public int $onhold = 0;
/** How many iterations has this job been in process in the queue */ /**
* How many iterations has this job been in process in the queue.
*
* @var int
* @since 1.0.0
*/
public int $inprocessing = 0; public int $inprocessing = 0;
/**
* What is the deadline for this job?
*
* @param \DateTime
* @since 1.0.0
*/
public \DateTime $deadline; public \DateTime $deadline;
/**
* Which steps must be taken during the job execution
*
* @var JobStep[]
* @since 1.0.0
*/
public array $steps = []; public array $steps = [];
/**
* Constructor.
*
* @since 1.0.0
*/
public function __construct() public function __construct()
{ {
$this->deadline = new \DateTime('now'); $this->deadline = new \DateTime('now');
} }
public function getProfit() /**
* Get the profit of the job
*
* @return float
*
* @since 1.0.0
*/
public function getProfit() : float
{ {
return $this->value - $this->cost; return $this->value - $this->cost;
} }

View File

@ -24,8 +24,24 @@ namespace phpOMS\Scheduling;
*/ */
final class ScheduleQueue final class ScheduleQueue
{ {
/**
* Queue
*
* @var Job[]
* @since 1.0.0
*/
public array $queue = []; public array $queue = [];
/**
* Get element from queue
*
* @param int $size Amount of elements to return
* @param int $type Priority type to use for return
*
* @return Job[]
*
* @since 1.0.0
*/
public function get(int $size = 1, int $type = PriorityMode::FIFO) : array public function get(int $size = 1, int $type = PriorityMode::FIFO) : array
{ {
$jobs = []; $jobs = [];
@ -103,11 +119,33 @@ final class ScheduleQueue
return $jobs; return $jobs;
} }
/**
* Insert new element into queue
*
* @param int $id Element id
* @param Job $job Element to add
*
* @return void
*
* @since 1.0.0
*/
public function insert(int $id, Job $job) : void public function insert(int $id, Job $job) : void
{ {
$this->queue[$id] = $job; $this->queue[$id] = $job;
} }
/**
* Pop elements from the queue.
*
* This also removes the elements from the queue
*
* @param int $size Amount of elements to return
* @param int $type Priority type to use for return
*
* @return Job[]
*
* @since 1.0.0
*/
public function pop(int $size = 1, int $type = PriorityMode::FIFO) : array public function pop(int $size = 1, int $type = PriorityMode::FIFO) : array
{ {
$jobs = $this->get($size, $type); $jobs = $this->get($size, $type);
@ -118,6 +156,15 @@ final class ScheduleQueue
return $jobs; return $jobs;
} }
/**
* Increases the hold counter of an element
*
* @param int $id Id of the element (0 = all elements)
*
* @return void
*
* @since 1.0.0
*/
public function bumpHold(int $id = 0) : void public function bumpHold(int $id = 0) : void
{ {
if ($id === 0) { if ($id === 0) {
@ -129,6 +176,16 @@ final class ScheduleQueue
} }
} }
/**
* Change the priority of an element
*
* @param int $id Id of the element (0 = all elements)
* @param float $priority Priority to increase by
*
* @return void
*
* @since 1.0.0
*/
public function adjustPriority(int $id = 0, float $priority = 0.1) : void public function adjustPriority(int $id = 0, float $priority = 0.1) : void
{ {
if ($id === 0) { if ($id === 0) {
@ -140,7 +197,16 @@ final class ScheduleQueue
} }
} }
public function remove(string $id) : void /**
* Remove an element from the queue
*
* @param int $id Id of the element
*
* @return void
*
* @since 1.0.0
*/
public function remove(int $id) : void
{ {
unset($this->queue[$id]); unset($this->queue[$id]);
} }

View File

@ -22,8 +22,8 @@ use phpOMS\Math\Stochastic\Distribution\NormalDistribution;
* @package phpOMS\Algorithm\Rating * @package phpOMS\Algorithm\Rating
* @license OMS License 2.0 * @license OMS License 2.0
* @link https://jingga.app * @link https://jingga.app
* @since 1.0.0
* @see https://www.moserware.com/assets/computing-your-skill/The%20Math%20Behind%20TrueSkill.pdf * @see https://www.moserware.com/assets/computing-your-skill/The%20Math%20Behind%20TrueSkill.pdf
* @since 1.0.0
* *
* @todo Implement https://github.com/sublee/trueskill/blob/master/trueskill/__init__.py * @todo Implement https://github.com/sublee/trueskill/blob/master/trueskill/__init__.py
* https://github.com/Karaka-Management/phpOMS/issues/337 * https://github.com/Karaka-Management/phpOMS/issues/337
@ -50,6 +50,17 @@ class TrueSkill
private float $drawProbability = 0.0; private float $drawProbability = 0.0;
/**
* Constructor.
*
* @param null|float $mu Mu
* @param null|float $sigma Sigma
* @param null|float $beta Beta
* @param null|float $tau Tau
* @param null|float $drawProbability Draw probability
*
* @since 1.0.0
*/
public function __construct( public function __construct(
?float $mu = null, ?float $mu = null,
?float $sigma = null, ?float $sigma = null,
@ -64,7 +75,18 @@ class TrueSkill
$this->drawProbability = $drawProbability ?? self::DEFAULT_DRAW_PROBABILITY; $this->drawProbability = $drawProbability ?? self::DEFAULT_DRAW_PROBABILITY;
} }
public function winProbability(array $team1, array $team2, float $drawMargin = 0.0) /**
* Calculate win probability
*
* @param array $team1 Team 1
* @param array $team2 Team 2
* @param float $drawMargin Draw margin
*
* @return float
*
* @since 1.0.0
*/
public function winProbability(array $team1, array $team2, float $drawMargin = 0.0) : float
{ {
$sigmaSum = 0.0; $sigmaSum = 0.0;
$mu1 = 0.0; $mu1 = 0.0;
@ -204,22 +226,37 @@ class TrueSkill
/ (NormalDistribution::getCdf($epsilon - $tAbs, 0.0, 1.0) - NormalDistribution::getCdf(-$epsilon - $tAbs, 0.0, 1.0)); / (NormalDistribution::getCdf($epsilon - $tAbs, 0.0, 1.0) - NormalDistribution::getCdf(-$epsilon - $tAbs, 0.0, 1.0));
} }
/**
*
*/
private function buildRatingLayer() : void private function buildRatingLayer() : void
{ {
} }
/**
*
*/
private function buildPerformanceLayer() : void private function buildPerformanceLayer() : void
{ {
} }
/**
*
*/
private function buildTeamPerformanceLayer() : void private function buildTeamPerformanceLayer() : void
{ {
} }
/**
*
*/
private function buildTruncLayer() : void private function buildTruncLayer() : void
{ {
} }
/**
*
*/
private function factorGraphBuilders() private function factorGraphBuilders()
{ {
// Rating layer // Rating layer
@ -238,6 +275,9 @@ class TrueSkill
]; ];
} }
/**
*
*/
public function rating() : void public function rating() : void
{ {
// Start values // Start values

View File

@ -0,0 +1,28 @@
<?php
/**
* Jingga
*
* PHP Version 8.1
*
* @package phpOMS\Algorithm\Rating
* @copyright Dennis Eichhorn
* @license OMS License 2.0
* @version 1.0.0
* @link https://jingga.app
*/
declare(strict_types=1);
namespace phpOMS\Algorithm\Rating;
/**
* Elo rating calculation using Elo rating
*
* @package phpOMS\Algorithm\Rating
* @license OMS License 2.0
* @link https://jingga.app
* @see https://en.wikipedia.org/wiki/Elo_rating_system
* @since 1.0.0
*/
final class TrueSkillFactoryGraph
{
}

View File

@ -39,7 +39,7 @@ final class ShippingFactory
{ {
switch ($type) { switch ($type) {
case ShippingType::DHL: case ShippingType::DHL:
return new \phpOMS\Api\Shipping\DHL\DHLShipping(); return new \phpOMS\Api\Shipping\DHL\DHLInternationalShipping();
case ShippingType::DPD: case ShippingType::DPD:
return new \phpOMS\Api\Shipping\DPD\DPDShipping(); return new \phpOMS\Api\Shipping\DPD\DPDShipping();
case ShippingType::FEDEX: case ShippingType::FEDEX:

View File

@ -37,9 +37,15 @@ final class BayesianPersonalizedRanking
private array $itemFactors = []; private array $itemFactors = [];
// num_factors determines the dimensionality of the latent factor space. /**
// learning_rate controls the step size for updating the latent factors during optimization. * Constructor.
// regularization prevents over-fitting by adding a penalty for large parameter values. *
* @param int $numFactors Determines the dimensionality of the latent factor space.
* @param float $learningRate Controls the step size for updating the latent factors during optimization.
* @param float $regularization Prevents over-fitting by adding a penalty for large parameter values.
*
* @since 1.0.0
*/
public function __construct(int $numFactors, float $learningRate, float $regularization) public function __construct(int $numFactors, float $learningRate, float $regularization)
{ {
$this->numFactors = $numFactors; $this->numFactors = $numFactors;
@ -47,7 +53,14 @@ final class BayesianPersonalizedRanking
$this->regularization = $regularization; $this->regularization = $regularization;
} }
private function generateRandomFactors() /**
* Calculate random factors
*
* @return array
*
* @since 1.0.0
*/
private function generateRandomFactors() : array
{ {
$factors = []; $factors = [];
for ($i = 0; $i < $this->numFactors; ++$i) { for ($i = 0; $i < $this->numFactors; ++$i) {
@ -57,6 +70,9 @@ final class BayesianPersonalizedRanking
return $factors; return $factors;
} }
/**
* @todo implement
*/
public function predict($userId, $itemId) { public function predict($userId, $itemId) {
$userFactor = $this->userFactors[$userId]; $userFactor = $this->userFactors[$userId];
$itemFactor = $this->itemFactors[$itemId]; $itemFactor = $this->itemFactors[$itemId];
@ -69,6 +85,9 @@ final class BayesianPersonalizedRanking
return $score; return $score;
} }
/**
* @todo implement
*/
public function updateFactors($userId, $posItemId, $negItemId) : void public function updateFactors($userId, $posItemId, $negItemId) : void
{ {
if (!isset($this->userFactors[$userId])) { if (!isset($this->userFactors[$userId])) {

View File

@ -24,16 +24,6 @@ namespace phpOMS\Business\Sales;
*/ */
final class OrderSuggestion final class OrderSuggestion
{ {
/**
* Constructor
*
* @since 1.0.0
* @codeCoverageIgnore
*/
private function __construct()
{
}
/** /**
* Calculate the optimal order quantity using the Andler formula * Calculate the optimal order quantity using the Andler formula
*/ */

View File

@ -158,8 +158,23 @@ abstract class DataMapperAbstract
$this->db = $db; $this->db = $db;
} }
/**
* Column name of the index
*
* @var string
* @since 1.0.0
*/
protected string $indexedBy = ''; protected string $indexedBy = '';
/**
* Set column name where the id is defined
*
* @param string $index Column name of the index
*
* @return self
*
* @since 1.0.0
*/
public function indexedBy(string $index) : self public function indexedBy(string $index) : self
{ {
$this->indexedBy = $index; $this->indexedBy = $index;

View File

@ -474,15 +474,20 @@ class DataMapperFactory
return static::FACTORY::createWith($data); return static::FACTORY::createWith($data);
} }
/**
* Mapper uses a factory
*
* @return bool
*
* @since 1.0.0
*/
public static function hasFactory() : bool public static function hasFactory() : bool
{ {
return !empty(static::FACTORY); return !empty(static::FACTORY);
} }
/** /**
* Create the empty base model * Get base model class name
*
* @param null|array $data Data to use for initialization
* *
* @return string * @return string
* *

View File

@ -748,9 +748,9 @@ final class ReadMapper extends DataMapperAbstract
} }
/** @var self $relMapper */ /** @var self $relMapper */
$relMapper = $this->createRelationMapper($rel['mapper']::reader(db: $this->db), $member); $relMapper = $this->createRelationMapper($rel['mapper']::reader(db: $this->db), $member);
$relMapper->depth = $this->depth + 1; $relMapper->depth = $this->depth + 1;
$relMapper->type = $this->type; $relMapper->type = $this->type;
$relMapper->joinAlias = '_' . $member; $relMapper->joinAlias = '_' . $member;
// Here we go further into the depth of the model (e.g. a hasMany/ownsOne can again have ownsOne...) // Here we go further into the depth of the model (e.g. a hasMany/ownsOne can again have ownsOne...)
@ -1090,8 +1090,8 @@ final class ReadMapper extends DataMapperAbstract
// in this case you can get the profile by loading the profile based on the account reference column // in this case you can get the profile by loading the profile based on the account reference column
/** @var self $belongsToMapper */ /** @var self $belongsToMapper */
$belongsToMapper = $this->createRelationMapper($mapper::get($this->db), $member); $belongsToMapper = $this->createRelationMapper($mapper::get($this->db), $member);
$belongsToMapper->depth = $this->depth + 1; $belongsToMapper->depth = $this->depth + 1;
$belongsToMapper->joinAlias = '_' . $member; $belongsToMapper->joinAlias = '_' . $member;
$belongsToMapper->where( $belongsToMapper->where(

View File

@ -181,7 +181,7 @@ final class UpdateMapper extends DataMapperAbstract
\usleep(10000); \usleep(10000);
$repeat = true; $repeat = true;
} }
} while($repeat); } while ($repeat);
} catch (\Throwable $t) { } catch (\Throwable $t) {
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
\phpOMS\Log\FileLogger::getInstance()->error( \phpOMS\Log\FileLogger::getInstance()->error(
@ -210,19 +210,48 @@ final class UpdateMapper extends DataMapperAbstract
/** @var class-string<DataMapperFactory> $mapper */ /** @var class-string<DataMapperFactory> $mapper */
$mapper = $this->mapper::BELONGS_TO[$propertyName]['mapper']; $mapper = $this->mapper::BELONGS_TO[$propertyName]['mapper'];
if (!isset($this->with[$propertyName])) { if (isset($this->with[$propertyName])) {
$id = $mapper::getObjectId($obj); /** @var self $relMapper */
$relMapper = $this->createRelationMapper($mapper::update(db: $this->db), $propertyName);
$relMapper->depth = $this->depth + 1;
return empty($id) && $mapper::isNullModel($obj) $id = $relMapper->execute($obj);
? null
: $id; if (!isset($this->mapper::OWNS_ONE[$propertyName]['by'])) {
return $id;
}
if ($this->mapper::OWNS_ONE[$propertyName]['private'] ?? false) {
$refClass = new \ReflectionClass($obj);
$refProp = $refClass->getProperty($this->mapper::OWNS_ONE[$propertyName]['by']);
$value = $refProp->getValue($obj);
} else {
$value = $obj->{$this->mapper::OWNS_ONE[$propertyName]['by']};
}
return $value;
} }
/** @var self $relMapper */ if (isset($this->mapper::BELONGS_TO[$propertyName]['by'])) {
$relMapper = $this->createRelationMapper($mapper::update(db: $this->db), $propertyName); // has by (obj is stored as a different model e.g. model = profile but reference/db is account)
$relMapper->depth = $this->depth + 1; if ($this->mapper::BELONGS_TO[$propertyName]['private'] ?? false) {
$refClass = new \ReflectionClass($obj);
$refProp = $refClass->getProperty($this->mapper::BELONGS_TO[$propertyName]['by']);
$obj = $refProp->getValue($obj);
} else {
$obj = $obj->{$this->mapper::BELONGS_TO[$propertyName]['by']};
}
return $relMapper->execute($obj); if (!\is_object($obj)) {
return $obj;
}
}
$id = $mapper::getObjectId($obj);
return empty($id) && $mapper::isNullModel($obj)
? null
: $id;
} }
/** /**
@ -240,19 +269,48 @@ final class UpdateMapper extends DataMapperAbstract
/** @var class-string<DataMapperFactory> $mapper */ /** @var class-string<DataMapperFactory> $mapper */
$mapper = $this->mapper::OWNS_ONE[$propertyName]['mapper']; $mapper = $this->mapper::OWNS_ONE[$propertyName]['mapper'];
if (!isset($this->with[$propertyName])) { if (isset($this->with[$propertyName])) {
$id = $mapper::getObjectId($obj); /** @var self $relMapper */
$relMapper = $this->createRelationMapper($mapper::update(db: $this->db), $propertyName);
$relMapper->depth = $this->depth + 1;
return empty($id) && $mapper::isNullModel($obj) $id = $relMapper->execute($obj);
? null
: $id; if (!isset($this->mapper::OWNS_ONE[$propertyName]['by'])) {
return $id;
}
if ($this->mapper::OWNS_ONE[$propertyName]['private'] ?? false) {
$refClass = new \ReflectionClass($obj);
$refProp = $refClass->getProperty($this->mapper::OWNS_ONE[$propertyName]['by']);
$value = $refProp->getValue($obj);
} else {
$value = $obj->{$this->mapper::OWNS_ONE[$propertyName]['by']};
}
return $value;
} }
/** @var self $relMapper */ if (isset($this->mapper::OWNS_ONE[$propertyName]['by'])) {
$relMapper = $this->createRelationMapper($mapper::update(db: $this->db), $propertyName); // has by (obj is stored as a different model e.g. model = profile but reference/db is account)
$relMapper->depth = $this->depth + 1; if ($this->mapper::OWNS_ONE[$propertyName]['private'] ?? false) {
$refClass = new \ReflectionClass($obj);
$refProp = $refClass->getProperty($this->mapper::OWNS_ONE[$propertyName]['by']);
$obj = $refProp->getValue($obj);
} else {
$obj = $obj->{$this->mapper::OWNS_ONE[$propertyName]['by']};
}
return $relMapper->execute($obj); if (!\is_object($obj)) {
return $obj;
}
}
$id = $mapper::getObjectId($obj);
return empty($id) && $mapper::isNullModel($obj)
? null
: $id;
} }
/** /**

View File

@ -177,7 +177,7 @@ final class WriteMapper extends DataMapperAbstract
\usleep(10000); \usleep(10000);
$repeat = true; $repeat = true;
} }
} while($repeat); } while ($repeat);
$objId = empty($id = $this->mapper::getObjectId($obj)) ? $this->db->con->lastInsertId() : $id; $objId = empty($id = $this->mapper::getObjectId($obj)) ? $this->db->con->lastInsertId() : $id;
\settype($objId, $this->mapper::COLUMNS[$this->mapper::PRIMARYFIELD]['type']); \settype($objId, $this->mapper::COLUMNS[$this->mapper::PRIMARYFIELD]['type']);
@ -210,19 +210,35 @@ final class WriteMapper extends DataMapperAbstract
*/ */
private function createOwnsOne(string $propertyName, object $obj) : mixed private function createOwnsOne(string $propertyName, object $obj) : mixed
{ {
if (!\is_object($obj)) { // @question This code prevents us from EVER creating an object with a 'by' reference since we always assume
return $obj; // that it already exists -> only return the custom reference id
// See bug below.
// @todo We might also have to handle 'column'
if (isset($this->mapper::OWNS_ONE[$propertyName]['by'])) {
// has by (obj is stored as a different model e.g. model = profile but reference/db is account)
if ($this->mapper::OWNS_ONE[$propertyName]['private'] ?? false) {
$refClass = new \ReflectionClass($obj);
$refProp = $refClass->getProperty($this->mapper::OWNS_ONE[$propertyName]['by']);
$obj = $refProp->getValue($obj);
} else {
$obj = $obj->{$this->mapper::OWNS_ONE[$propertyName]['by']};
}
if (!\is_object($obj)) {
return $obj;
}
} }
/** @var class-string<DataMapperFactory> $mapper */ /** @var class-string<DataMapperFactory> $mapper */
$mapper = $this->mapper::OWNS_ONE[$propertyName]['mapper']; $mapper = $this->mapper::OWNS_ONE[$propertyName]['mapper'];
$primaryKey = $mapper::getObjectId($obj); $primaryKey = $mapper::getObjectId($obj);
if (empty($primaryKey)) { // @bug The $mapper::create() might cause a problem if 'by' is set.
return $mapper::create(db: $this->db)->execute($obj); // This is because we don't want to create this obj but the child obj.
} return empty($primaryKey)
? $mapper::create(db: $this->db)->execute($obj)
return $primaryKey; : $primaryKey;
} }
/** /**
@ -237,13 +253,11 @@ final class WriteMapper extends DataMapperAbstract
*/ */
private function createBelongsTo(string $propertyName, object $obj) : mixed private function createBelongsTo(string $propertyName, object $obj) : mixed
{ {
if (!\is_object($obj)) { // @question This code prevents us from EVER creating an object with a 'by' reference since we always assume
return $obj; // that it already exists -> only return the custom reference id
} // See bug below.
$mapper = '';
$primaryKey = 0;
// @todo We might also have to handle 'column'
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'] ?? false) { if ($this->mapper::BELONGS_TO[$propertyName]['private'] ?? false) {
@ -253,6 +267,10 @@ final class WriteMapper extends DataMapperAbstract
} else { } else {
$obj = $obj->{$this->mapper::BELONGS_TO[$propertyName]['by']}; $obj = $obj->{$this->mapper::BELONGS_TO[$propertyName]['by']};
} }
if (!\is_object($obj)) {
return $obj;
}
} }
/** @var class-string<DataMapperFactory> $mapper */ /** @var class-string<DataMapperFactory> $mapper */
@ -435,7 +453,7 @@ final class WriteMapper extends DataMapperAbstract
\usleep(10000); \usleep(10000);
$repeat = true; $repeat = true;
} }
} while($repeat); } while ($repeat);
} catch (\Throwable $t) { } catch (\Throwable $t) {
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
\phpOMS\Log\FileLogger::getInstance()->error( \phpOMS\Log\FileLogger::getInstance()->error(

View File

@ -1296,7 +1296,7 @@ class Builder extends BuilderAbstract
* *
* @param string|array $columns Columns to join on * @param string|array $columns Columns to join on
* @param null|string|array $operator Comparison operator * @param null|string|array $operator Comparison operator
* @param null|string|array $values Values to compare with * @param mixed $values Values to compare with
* @param string|array $boolean Concatenation * @param string|array $boolean Concatenation
* @param null|string $table Table this belongs to * @param null|string $table Table this belongs to
* *

View File

@ -24,8 +24,21 @@ namespace phpOMS\DataStorage\Database\Query;
*/ */
final class ColumnName final class ColumnName
{ {
/**
* Column name
*
* @var string
* @since 1.0.0
*/
public string $name = ''; public string $name = '';
/**
* Constructor.
*
* @param string $name Column name
*
* @since 1.0.0
*/
public function __construct(string $name) public function __construct(string $name)
{ {
$this->name = $name; $this->name = $name;

View File

@ -43,10 +43,21 @@ class Concat extends Builder
$this->type = QueryType::SELECT; $this->type = QueryType::SELECT;
} }
/**
* Columns to concat
*
* @param string $as Alias
* @param string $delim Delimiter
* @param string ...$columns Columns to concat
*
* @return void
*
* @since 1.0.0
*/
public function columns(string $as, string $delim, ...$columns) : void public function columns(string $as, string $delim, ...$columns) : void
{ {
$this->delim = $delim; $this->delim = $delim;
$this->as = $as; $this->as = $as;
$this->select($columns); $this->select($columns);
} }
@ -64,4 +75,4 @@ class Concat extends Builder
return $query; return $query;
} }
} }

View File

@ -295,7 +295,7 @@ class Grammar extends GrammarAbstract
protected function compileWhereElement(array $element, Builder $query, bool $first = true) : string protected function compileWhereElement(array $element, Builder $query, bool $first = true) : string
{ {
$expression = ''; $expression = '';
$prefix = ''; $prefix = '';
if (!$first) { if (!$first) {
$prefix = ' ' . \strtoupper($element['boolean']) . ' '; $prefix = ' ' . \strtoupper($element['boolean']) . ' ';
@ -319,7 +319,7 @@ class Grammar extends GrammarAbstract
if (empty($element['value'])) { if (empty($element['value'])) {
$element['operator'] = '='; $element['operator'] = '=';
$element['value'] = null; $element['value'] = null;
} }
} }

View File

@ -44,6 +44,8 @@ class ISO4217CharEnum extends Enum
public const _AZN = 'AZN'; public const _AZN = 'AZN';
public const _AZM = 'AZM';
public const _BSD = 'BSD'; public const _BSD = 'BSD';
public const _BBD = 'BBD'; public const _BBD = 'BBD';
@ -75,7 +77,9 @@ class ISO4217CharEnum extends Enum
public const _CLP = 'CLP'; public const _CLP = 'CLP';
public const _CNY = 'CNY'; public const _CNY = 'CNY';
public const _CNH = 'CNH'; public const _CNH = 'CNH';
public const _RMB = 'RMB'; public const _RMB = 'RMB';
public const _COP = 'COP'; public const _COP = 'COP';

View File

@ -46,6 +46,8 @@ class ISO4217DecimalEnum extends Enum
public const _AZN = 2; public const _AZN = 2;
public const _AZM = 2;
public const _BAM = 2; public const _BAM = 2;
public const _BBD = 2; public const _BBD = 2;
@ -93,7 +95,9 @@ class ISO4217DecimalEnum extends Enum
public const _CLP = 0; public const _CLP = 0;
public const _CNY = 2; public const _CNY = 2;
public const _CNH = 2; public const _CNH = 2;
public const _RMB = 2; public const _RMB = 2;
public const _COP = 2; public const _COP = 2;

View File

@ -42,6 +42,8 @@ class ISO4217Enum extends Enum
public const _AZN = 'New Manats, Azerbaijan'; public const _AZN = 'New Manats, Azerbaijan';
public const _AZM = 'Manats, Azerbaijan';
public const _AOA = 'Kwanza, Angolan'; public const _AOA = 'Kwanza, Angolan';
public const _BSD = 'Dollars, Bahamas'; public const _BSD = 'Dollars, Bahamas';
@ -75,7 +77,9 @@ class ISO4217Enum extends Enum
public const _CLP = 'Pesos, Chile'; public const _CLP = 'Pesos, Chile';
public const _CNY = 'Yuan Renminbi, China'; public const _CNY = 'Yuan Renminbi, China';
public const _CNH = 'Yuan Renminbi, China'; public const _CNH = 'Yuan Renminbi, China';
public const _RMB = 'Yuan Renminbi, China'; public const _RMB = 'Yuan Renminbi, China';
public const _COP = 'Pesos, Colombia'; public const _COP = 'Pesos, Colombia';

View File

@ -46,6 +46,8 @@ class ISO4217NumEnum extends Enum
public const _AZN = '944'; public const _AZN = '944';
public const _AZM = '944';
public const _BAM = '977'; public const _BAM = '977';
public const _BBD = '052'; public const _BBD = '052';
@ -93,7 +95,9 @@ class ISO4217NumEnum extends Enum
public const _CLP = '152'; public const _CLP = '152';
public const _CNY = '156'; public const _CNY = '156';
public const _CNH = '156'; public const _CNH = '156';
public const _RMB = '156'; public const _RMB = '156';
public const _COP = '170'; public const _COP = '170';
@ -396,5 +400,7 @@ class ISO4217NumEnum extends Enum
public const _ZWL = '932'; public const _ZWL = '932';
public const _ZWD = '716';
use ISO4217Trait; use ISO4217Trait;
} }

View File

@ -83,7 +83,9 @@ class ISO4217SubUnitEnum extends Enum
public const _CLP = 100; public const _CLP = 100;
public const _CNY = 100; public const _CNY = 100;
public const _CNH = 100; public const _CNH = 100;
public const _RMB = 100; public const _RMB = 100;
public const _COP = 100; public const _COP = 100;
@ -402,6 +404,8 @@ class ISO4217SubUnitEnum extends Enum
public const _AZN = 100; public const _AZN = 100;
public const _AZM = 100;
public const _CRC = 100; public const _CRC = 100;
public const _GIP = 100; public const _GIP = 100;

View File

@ -17,7 +17,7 @@ namespace phpOMS\Localization;
use phpOMS\Stdlib\Base\Enum; use phpOMS\Stdlib\Base\Enum;
/** /**
* Country symbols ISO list. * Currency symbols ISO list.
* *
* @package phpOMS\Localization * @package phpOMS\Localization
* @license OMS License 2.0 * @license OMS License 2.0
@ -42,6 +42,8 @@ class ISO4217SymbolEnum extends Enum
public const _AZN = 'ман'; public const _AZN = 'ман';
public const _AZM = '₼';
public const _AOA = 'Kz'; public const _AOA = 'Kz';
public const _BSD = '$'; public const _BSD = '$';
@ -75,7 +77,9 @@ class ISO4217SymbolEnum extends Enum
public const _CLP = '$'; public const _CLP = '$';
public const _CNY = '¥'; public const _CNY = '¥';
public const _CNH = '¥'; public const _CNH = '¥';
public const _RMB = '¥'; public const _RMB = '¥';
public const _COP = '$'; public const _COP = '$';
@ -108,6 +112,8 @@ class ISO4217SymbolEnum extends Enum
public const _GIP = '£'; public const _GIP = '£';
public const _GMD = 'GMD';
public const _GTQ = 'Q'; public const _GTQ = 'Q';
public const _GGP = '£'; public const _GGP = '£';
@ -302,7 +308,7 @@ class ISO4217SymbolEnum extends Enum
public const _HTG = 'HTG'; public const _HTG = 'HTG';
public const _IQD = 'IQD'; public const _IQD = 'ع';
public const _JOD = 'JOD'; public const _JOD = 'JOD';

View File

@ -29,7 +29,7 @@ trait ISO4217Trait
* *
* @param string $country Country 2 code * @param string $country Country 2 code
* *
* @return array * @return string
* *
* @since 1.0.0 * @since 1.0.0
*/ */
@ -65,7 +65,7 @@ trait ISO4217Trait
case ISO3166TwoEnum::_AUT: case ISO3166TwoEnum::_AUT:
return self::_EUR; return self::_EUR;
case ISO3166TwoEnum::_AZE: case ISO3166TwoEnum::_AZE:
return self::_AZM; return self::_AZN;
case ISO3166TwoEnum::_PRT: case ISO3166TwoEnum::_PRT:
return self::_EUR; return self::_EUR;
case ISO3166TwoEnum::_BHS: case ISO3166TwoEnum::_BHS:
@ -235,7 +235,7 @@ trait ISO4217Trait
case ISO3166TwoEnum::_IDN: case ISO3166TwoEnum::_IDN:
return self::_IDR; return self::_IDR;
case ISO3166TwoEnum::_IRQ: case ISO3166TwoEnum::_IRQ:
return self::_NID; return self::_IQD;
case ISO3166TwoEnum::_IRL: case ISO3166TwoEnum::_IRL:
return self::_EUR; return self::_EUR;
case ISO3166TwoEnum::_ISR: case ISO3166TwoEnum::_ISR:
@ -325,7 +325,7 @@ trait ISO4217Trait
case ISO3166TwoEnum::_MAR: case ISO3166TwoEnum::_MAR:
return self::_MAD; return self::_MAD;
case ISO3166TwoEnum::_MOZ: case ISO3166TwoEnum::_MOZ:
return self::_MZM; return self::_MZN;
case ISO3166TwoEnum::_NAM: case ISO3166TwoEnum::_NAM:
return self::_NAD; return self::_NAD;
case ISO3166TwoEnum::_NPL: case ISO3166TwoEnum::_NPL:
@ -371,7 +371,7 @@ trait ISO4217Trait
case ISO3166TwoEnum::_REU: case ISO3166TwoEnum::_REU:
return self::_EUR; return self::_EUR;
case ISO3166TwoEnum::_ROU: case ISO3166TwoEnum::_ROU:
return self::_ROL; return self::_RON;
case ISO3166TwoEnum::_RUS: case ISO3166TwoEnum::_RUS:
return self::_RUB; return self::_RUB;
case ISO3166TwoEnum::_RWA: case ISO3166TwoEnum::_RWA:
@ -417,7 +417,7 @@ trait ISO4217Trait
case ISO3166TwoEnum::_VCT: case ISO3166TwoEnum::_VCT:
return self::_XCD; return self::_XCD;
case ISO3166TwoEnum::_SUR: case ISO3166TwoEnum::_SUR:
return self::_SRG; return self::_SRD;
case ISO3166TwoEnum::_SWZ: case ISO3166TwoEnum::_SWZ:
return self::_SZL; return self::_SZL;
case ISO3166TwoEnum::_SWE: case ISO3166TwoEnum::_SWE:
@ -465,7 +465,7 @@ trait ISO4217Trait
case ISO3166TwoEnum::_VAT: case ISO3166TwoEnum::_VAT:
return self::_EUR; return self::_EUR;
case ISO3166TwoEnum::_VEN: case ISO3166TwoEnum::_VEN:
return self::_VEB; return self::_VEF;
case ISO3166TwoEnum::_VNM: case ISO3166TwoEnum::_VNM:
return self::_VND; return self::_VND;
case ISO3166TwoEnum::_WLF: case ISO3166TwoEnum::_WLF:

View File

@ -196,6 +196,16 @@ final class Functions
return $a % $b; return $a % $b;
} }
/**
* Modular implementation for float values
*
* @param float $a a
* @param float $b b
*
* @return float
*
* @since 1.0.0
*/
public static function modFloat(float $a, float $b) : float public static function modFloat(float $a, float $b) : float
{ {
return $a - ((int) ($a / $b)) * $b; return $a - ((int) ($a / $b)) * $b;

View File

@ -34,7 +34,7 @@ use phpOMS\Validation\Network\Email as EmailValidator;
* @link https://jingga.app * @link https://jingga.app
* @since 1.0.0 * @since 1.0.0
*/ */
class Email implements MessageInterface class Email
{ {
/** /**
* Mailer name. * Mailer name.
@ -462,11 +462,25 @@ class Email implements MessageInterface
$this->contentType = $isHtml ? MimeType::M_HTML : MimeType::M_TEXT; $this->contentType = $isHtml ? MimeType::M_HTML : MimeType::M_TEXT;
} }
/**
* Get content type
*
* @return string
*
* @since 1.0.0
*/
public function getContentType() : string public function getContentType() : string
{ {
return $this->contentType; return $this->contentType;
} }
/**
* Is html content type?
*
* @return bool
*
* @since 1.0.0
*/
public function isHtml() : bool public function isHtml() : bool
{ {
return $this->contentType === MimeType::M_HTML; return $this->contentType === MimeType::M_HTML;
@ -568,10 +582,10 @@ class Email implements MessageInterface
* *
* @since 1.0.0 * @since 1.0.0
*/ */
public static function parseAddresses(string $addrstr, bool $useimap = true, string $charset = CharsetType::ISO_8859_1) : array public static function parseAddresses(string $addrstr, bool $useImap = true, string $charset = CharsetType::ISO_8859_1) : array
{ {
$addresses = []; $addresses = [];
if ($useimap && \function_exists('imap_rfc822_parse_adrlist')) { if ($useImap && \function_exists('imap_rfc822_parse_adrlist')) {
$list = \imap_rfc822_parse_adrlist($addrstr, ''); $list = \imap_rfc822_parse_adrlist($addrstr, '');
foreach ($list as $address) { foreach ($list as $address) {
if (($address->host !== '.SYNTAX-ERROR.') if (($address->host !== '.SYNTAX-ERROR.')
@ -623,6 +637,15 @@ class Email implements MessageInterface
return $addresses; return $addresses;
} }
/**
* Parse email template.
*
* Replaces placeholders with content
*
* @return void
*
* @since 1.0.0
*/
public function parseTemplate() : void public function parseTemplate() : void
{ {
if (empty($this->template)) { if (empty($this->template)) {
@ -778,7 +801,7 @@ class Email implements MessageInterface
$result .= 'X-Mailer: ' . self::XMAILER . self::$LE; $result .= 'X-Mailer: ' . self::XMAILER . self::$LE;
if ($this->confirmationAddress !== '') { if ($this->confirmationAddress !== '') {
$result .= 'Disposition-Notification-To: ' . '<' . $this->confirmationAddress . '>' . self::$LE; $result .= 'Disposition-Notification-To: <' . $this->confirmationAddress . '>' . self::$LE;
} }
// Add custom headers // Add custom headers
@ -1283,10 +1306,10 @@ class Email implements MessageInterface
$mime[] = empty($encodedName) $mime[] = empty($encodedName)
? \sprintf('Content-Disposition: %s%s', $disposition, self::$LE . self::$LE) ? \sprintf('Content-Disposition: %s%s', $disposition, self::$LE . self::$LE)
: \sprintf('Content-Disposition: %s; filename=%s%s', : \sprintf('Content-Disposition: %s; filename=%s%s',
$disposition, $disposition,
self::quotedString($encodedName), self::quotedString($encodedName),
self::$LE . self::$LE self::$LE . self::$LE
); );
} else { } else {
$mime[] = self::$LE; $mime[] = self::$LE;
} }
@ -1471,7 +1494,7 @@ class Email implements MessageInterface
* *
* @return void * @return void
* *
* @return 1.0.0 * @since 1.0.0
*/ */
public function setWordWrap() : void public function setWordWrap() : void
{ {
@ -1626,9 +1649,9 @@ class Email implements MessageInterface
$matchcount = \preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); $matchcount = \preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
break; break;
/* @noinspection PhpMissingBreakStatementInspection */
case 'comment': case 'comment':
$matchcount = \preg_match_all('/[()"]/', $str, $matches); $matchcount = \preg_match_all('/[()"]/', $str, $matches);
// no break
case 'text': case 'text':
default: default:
$matchcount += \preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); $matchcount += \preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
@ -1703,6 +1726,7 @@ class Email implements MessageInterface
break; break;
case 'comment': case 'comment':
$pattern = '\(\)"'; $pattern = '\(\)"';
// no break
case 'text': case 'text':
default: default:
// Replace every high ascii, control, =, ? and _ characters // Replace every high ascii, control, =, ? and _ characters
@ -2122,7 +2146,7 @@ class Email implements MessageInterface
/** /**
* Normalize line breaks in a string. * Normalize line breaks in a string.
* *
* @param string $text * @param string $text Text to normalize
* @param string $breaktype What kind of line break to use; defaults to self::$LE * @param string $breaktype What kind of line break to use; defaults to self::$LE
* *
* @return string * @return string
@ -2210,7 +2234,7 @@ class Email implements MessageInterface
/** /**
* Generate a DKIM signature. * Generate a DKIM signature.
* *
* @param string $signHeader * @param string $signHeader Sign header
* *
* @return string The DKIM signature value * @return string The DKIM signature value
* *

View File

@ -20,7 +20,6 @@ namespace phpOMS\Message\Mail;
use phpOMS\Security\Guard; use phpOMS\Security\Guard;
use phpOMS\System\SystemUtils; use phpOMS\System\SystemUtils;
use phpOMS\Utils\StringUtils;
use phpOMS\Validation\Network\Email as EmailValidator; use phpOMS\Validation\Network\Email as EmailValidator;
use phpOMS\Validation\Network\Hostname; use phpOMS\Validation\Network\Hostname;
@ -398,7 +397,6 @@ class MailHandler
* *
* @param string $to To * @param string $to To
* @param Email $mail Mail * @param Email $mail Mail
* @param string $body Message Body
* @param string $header Additional Header(s) * @param string $header Additional Header(s)
* @param null|string $params Params * @param null|string $params Params
* *

View File

@ -1,40 +0,0 @@
<?php
/**
* Jingga
*
* PHP Version 8.1
*
* @package phpOMS\Uri
* @copyright Dennis Eichhorn
* @license OMS License 2.0
* @version 1.0.0
* @link https://jingga.app
*/
declare(strict_types=1);
namespace phpOMS\Message\Mail;
/**
* Message interface.
*
* @property string $subject Subject
*
* @package phpOMS\Uri
* @license OMS License 2.0
* @link https://jingga.app
* @since 1.0.0
*/
interface MessageInterface
{
public function preSend(string $mailer) : bool;
public function addTo(string $address, string $name = '') : bool;
public function addCC(string $address, string $name = '') : bool;
public function addBCC(string $address, string $name = '') : bool;
public function addReplyTo(string $address, string $name = '') : bool;
public function setFrom(string $address, string $name = '') : bool;
}

View File

@ -312,8 +312,8 @@ abstract class ResponseAbstract implements \JsonSerializable, MessageInterface
/** /**
* Add response. * Add response.
* *
* @param mixed $key Response id * @param mixed $key Response id
* @param mixed $response Response to add * @param mixed $response Response to add
* *
* @return void * @return void
* *

View File

@ -44,7 +44,7 @@ final class ModuleManager
/** /**
* All modules that are running on this uri. * All modules that are running on this uri.
* *
* @var \phpOMS\Module\ModuleAbstract[] * @var array<string, array<string, \phpOMS\Module\ModuleAbstract>>
* @since 1.0.0 * @since 1.0.0
*/ */
private array $running = []; private array $running = [];
@ -678,9 +678,15 @@ final class ModuleManager
// Handle providing->receiving // Handle providing->receiving
foreach ($this->running as $mName => $controllers) { foreach ($this->running as $mName => $controllers) {
$controller = \reset($controllers); $controller = \reset($controllers);
if ($controller === false) {
continue;
}
foreach ($controller::$providing as $providing) { foreach ($controller::$providing as $providing) {
$ctrl = \reset($this->running[$providing]); $ctrl = \reset($this->running[$providing]);
if ($ctrl === false) {
continue;
}
if (!\in_array($mName, $ctrl->receiving)) { if (!\in_array($mName, $ctrl->receiving)) {
$ctrl->receiving[] = $mName; $ctrl->receiving[] = $mName;
@ -724,7 +730,7 @@ final class ModuleManager
) { ) {
try { try {
/** @var ModuleAbstract $obj */ /** @var ModuleAbstract $obj */
$obj = new $class($this->app); $obj = new $class($this->app);
$this->running[$module][$class] = $obj; $this->running[$module][$class] = $obj;
} catch (\Throwable $_) { } catch (\Throwable $_) {
$this->running[$module][$class] = new NullModule(); $this->running[$module][$class] = new NullModule();

View File

@ -134,6 +134,7 @@ abstract class StatusAbstract
* Init routes and hooks. * Init routes and hooks.
* *
* @param ModuleInfo $info Module info * @param ModuleInfo $info Module info
* @param string $type Is 'Routes' or 'Hooks'
* @param null|ApplicationInfo $appInfo Application info * @param null|ApplicationInfo $appInfo Application info
* *
* @return void * @return void
@ -204,6 +205,7 @@ abstract class StatusAbstract
* Deactivate routes and hooks. * Deactivate routes and hooks.
* *
* @param ModuleInfo $info Module info * @param ModuleInfo $info Module info
* @param string $type Is 'Routes' or 'Hooks'
* @param null|ApplicationInfo $appInfo Application info * @param null|ApplicationInfo $appInfo Application info
* *
* @return void * @return void

View File

@ -50,9 +50,11 @@ abstract class Enum
* *
* Checking if a given value is part of this enum * Checking if a given value is part of this enum
* *
* @param mixed $value Value to check * @template T
* *
* @return mixed * @param T $value Value to check
*
* @return null|T
* *
* @since 1.0.0 * @since 1.0.0
*/ */

View File

@ -23,6 +23,8 @@ use phpOMS\Contract\SerializableInterface;
* @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 The naming of functions in this class is atrocious (getInt, getFloat, getNormalized, ...).
*/ */
class FloatInt implements SerializableInterface class FloatInt implements SerializableInterface
{ {
@ -34,6 +36,12 @@ class FloatInt implements SerializableInterface
*/ */
public const MAX_DECIMALS = 4; public const MAX_DECIMALS = 4;
/**
* Divisor to get original value.
*
* @var int
* @since 1.0.0
*/
public const DIVISOR = 10000; public const DIVISOR = 10000;
/** /**
@ -92,7 +100,7 @@ class FloatInt implements SerializableInterface
public static function toInt(string $value, string $thousands = ',', string $decimal = '.') : int public static function toInt(string $value, string $thousands = ',', string $decimal = '.') : int
{ {
$newValue = $value; $newValue = $value;
$len = \strlen($value); $len = \strlen($value);
$decimalPos = \strrpos($value, $decimal); $decimalPos = \strrpos($value, $decimal);
if ($decimalPos === false) { if ($decimalPos === false) {
@ -140,20 +148,18 @@ class FloatInt implements SerializableInterface
return $this; return $this;
} }
/**
* Returns the value as float
*
* @return float
*
* @since 1.0.0
*/
public function getNormalizedValue() : float public function getNormalizedValue() : float
{ {
return $this->value / self::DIVISOR; return $this->value / self::DIVISOR;
} }
public function guessScalarValue() : int|float
{
$divider = self::DIVISOR;
return $this->value % $divider === 0
? (int) ($this->value / $divider)
: (float) ($this->value / $divider);
}
/** /**
* Get money. * Get money.
* *
@ -382,15 +388,24 @@ class FloatInt implements SerializableInterface
return $this; return $this;
} }
/**
* Identify the numeric format of a string
*
* @param string $str String representation
*
* @return null|array
*
* @since 1.0.0
*/
public static function identifyNumericFormat(string $str) : ?array public static function identifyNumericFormat(string $str) : ?array
{ {
$commaPos = \strrpos($str, ','); $commaPos = \strrpos($str, ',');
$periodPos = \strrpos($str, '.'); $periodPos = \strrpos($str, '.');
if ($commaPos !== false && $periodPos !== false) { if ($commaPos !== false && $periodPos !== false) {
return [ return [
'thousands' => $commaPos < $periodPos ? ',' : '.', 'thousands' => $commaPos < $periodPos ? ',' : '.',
'decimal' => $commaPos < $periodPos ? '.' : ',', 'decimal' => $commaPos < $periodPos ? '.' : ',',
]; ];
} elseif ($commaPos === false && $periodPos === false) { } elseif ($commaPos === false && $periodPos === false) {
return null; return null;
@ -403,7 +418,7 @@ class FloatInt implements SerializableInterface
return [ return [
'thousands' => $isComma ? '.' : ',', 'thousands' => $isComma ? '.' : ',',
'decimal' => $isComma ? ',' : '.' 'decimal' => $isComma ? ',' : '.',
]; ];
} }
} }

View File

@ -372,9 +372,9 @@ class SmartDateTime extends \DateTime
* *
* @since 1.0.0 * @since 1.0.0
*/ */
public static function startOfYear(int $month = 1) : SmartDateTime public static function startOfYear(int $month = 1) : self
{ {
return new SmartDateTime(\date('Y') . '-' . \sprintf('%02d', $month) . '-01'); return new self(\date('Y') . '-' . \sprintf('%02d', $month) . '-01');
} }
/** /**
@ -386,9 +386,9 @@ class SmartDateTime extends \DateTime
* *
* @since 1.0.0 * @since 1.0.0
*/ */
public static function endOfYear(int $month = 1) : SmartDateTime public static function endOfYear(int $month = 1) : self
{ {
return new SmartDateTime(\date('Y') . '-' . self::calculateMonthIndex(13 - $month, $month) . '-31'); return new self(\date('Y') . '-' . self::calculateMonthIndex(13 - $month, $month) . '-31');
} }
/** /**
@ -398,9 +398,9 @@ class SmartDateTime extends \DateTime
* *
* @since 1.0.0 * @since 1.0.0
*/ */
public static function startOfMonth() : SmartDateTime public static function startOfMonth() : self
{ {
return new SmartDateTime(\date('Y-m') . '-01'); return new self(\date('Y-m') . '-01');
} }
/** /**
@ -410,9 +410,9 @@ class SmartDateTime extends \DateTime
* *
* @since 1.0.0 * @since 1.0.0
*/ */
public static function endOfMonth() : SmartDateTime public static function endOfMonth() : self
{ {
return new SmartDateTime(\date('Y-m-t')); return new self(\date('Y-m-t'));
} }
/** /**
@ -449,6 +449,15 @@ class SmartDateTime extends \DateTime
return \abs(($mod < 0 ? 12 + $mod : $mod) % 12) + 1; return \abs(($mod < 0 ? 12 + $mod : $mod) % 12) + 1;
} }
/**
* Format duration in seconds as d:h:m:s
*
* @param int $duration Duration in seconds
*
* @return string
*
* @since 1.0.0
*/
public static function formatDuration(int $duration) : string public static function formatDuration(int $duration) : string
{ {
$days = \floor($duration / (24 * 3600)); $days = \floor($duration / (24 * 3600));

View File

@ -407,7 +407,11 @@ final class HttpUri implements UriInterface
if (empty($this->fragment)) { if (empty($this->fragment)) {
$this->uri .= $toAdd; $this->uri .= $toAdd;
} else { } else {
$this->uri = \substr_replace($this->uri, $toAdd, \strrpos($this->uri, '#'), 0); $pos = \strrpos($this->uri, '#');
if ($pos !== false) {
$this->uri = \substr_replace($this->uri, $toAdd, $pos, 0);
}
} }
} }

View File

@ -48,6 +48,10 @@ final class HtmlParser
$doc = new \DOMDocument(); $doc = new \DOMDocument();
$html = \file_get_contents($path); $html = \file_get_contents($path);
if ($html === false) {
return '';
}
$html = \preg_replace( $html = \preg_replace(
['~<style.*?</style>~', '~<script.*?</script>~'], ['~<style.*?</style>~', '~<script.*?</script>~'],
['', ''], ['', ''],
@ -63,8 +67,8 @@ final class HtmlParser
return empty($node->textContent) ? '' : $node->textContent; return empty($node->textContent) ? '' : $node->textContent;
} }
$content = ''; $content = '';
$xNode = new \DOMXpath($doc); $xNode = new \DOMXpath($doc);
$elements = $xNode->query($xpath); $elements = $xNode->query($xpath);
if ($elements === false) { if ($elements === false) {

View File

@ -5,9 +5,9 @@
* PHP Version 8.1 * PHP Version 8.1
* *
* @package phpOMS\Utils\Parser\Markdown * @package phpOMS\Utils\Parser\Markdown
* @license Original & extra license Emanuil Rusev, erusev.com (MIT) * @copyright Original & extra license Emanuil Rusev, erusev.com (MIT)
* @license Extended license Benjamin Hoegh (MIT) * @copyright Extended license Benjamin Hoegh (MIT)
* @license Extreme license doowzs (MIT) * @copyright Extreme license doowzs (MIT)
* @license This version: OMS License 2.0 * @license This version: OMS License 2.0
* @version 1.0.0 * @version 1.0.0
* @link https://jingga.app * @link https://jingga.app
@ -22,9 +22,9 @@ use phpOMS\Uri\UriFactory;
* Markdown parser class. * Markdown parser class.
* *
* @package phpOMS\Utils\Parser\Markdown * @package phpOMS\Utils\Parser\Markdown
* @license Original & extra license Emanuil Rusev, erusev.com (MIT) * @copyright Original & extra license Emanuil Rusev, erusev.com (MIT)
* @license Extended license Benjamin Hoegh (MIT) * @copyright Extended license Benjamin Hoegh (MIT)
* @license Extreme license doowzs (MIT) * @copyright Extreme license doowzs (MIT)
* @license This version: OMS License 2.0 * @license This version: OMS License 2.0
* @link https://jingga.app * @link https://jingga.app
* @see https://github.com/erusev/parsedown * @see https://github.com/erusev/parsedown
@ -336,7 +336,7 @@ class Markdown
/** /**
* TOC array after parsing headers * TOC array after parsing headers
* *
* @var array{text:string, id:string, level:string} * @var array{}|array{text:string, id:string, level:string}
* @since 1.0.0 * @since 1.0.0
*/ */
protected $contentsListArray = []; protected $contentsListArray = [];
@ -443,7 +443,7 @@ class Markdown
return self::$instances[$name]; return self::$instances[$name];
} }
$instance = new static(); $instance = new self();
self::$instances[$name] = $instance; self::$instances[$name] = $instance;
@ -594,7 +594,7 @@ class Markdown
$html = \trim($html, "\n"); $html = \trim($html, "\n");
// Merge consecutive dl elements // Merge consecutive dl elements
$html = \preg_replace('/<\/dl>\s+<dl>\s+/', '', $html); $html = \preg_replace('/<\/dl>\s+<dl>\s+/', '', $html) ?? '';
// Add footnotes // Add footnotes
if (isset($this->definitionData['Footnote'])) { if (isset($this->definitionData['Footnote'])) {
@ -651,7 +651,9 @@ class Markdown
public function contentsList($typeReturn = 'html') : string public function contentsList($typeReturn = 'html') : string
{ {
if (\strtolower($typeReturn) === 'json') { if (\strtolower($typeReturn) === 'json') {
return \json_encode($this->contentsListArray); $json = \json_encode($this->contentsListArray);
return $json === false ? '' : $json;
} }
$result = ''; $result = '';
@ -878,6 +880,7 @@ class Markdown
return null; return null;
} }
$matches = [];
if (($excerpt['text'][1] === '/' && \preg_match('/^<\/\w[\w-]*+[ ]*+>/s', $excerpt['text'], $matches)) if (($excerpt['text'][1] === '/' && \preg_match('/^<\/\w[\w-]*+[ ]*+>/s', $excerpt['text'], $matches))
|| ($excerpt['text'][1] === '!' && \preg_match('/^<!---?[^>-](?:-?+[^-])*-->/s', $excerpt['text'], $matches)) || ($excerpt['text'][1] === '!' && \preg_match('/^<!---?[^>-](?:-?+[^-])*-->/s', $excerpt['text'], $matches))
|| ($excerpt['text'][1] !== ' ' && \preg_match('/^<\w[\w-]*+(?:[ ]*+' . $this->regexHtmlAttribute . ')*+[ ]*+\/?>/s', $excerpt['text'], $matches)) || ($excerpt['text'][1] !== ' ' && \preg_match('/^<\w[\w-]*+(?:[ ]*+' . $this->regexHtmlAttribute . ')*+[ ]*+\/?>/s', $excerpt['text'], $matches))
@ -1332,8 +1335,11 @@ class Markdown
*/ */
protected function inlineEmbedding(array $excerpt) : ?array protected function inlineEmbedding(array $excerpt) : ?array
{ {
$video = false;
$audio = false;
if (!($this->options['embedding'] ?? false) if (!($this->options['embedding'] ?? false)
|| !(\str_starts_with($excerpt['text'], '[video') || \str_starts_with($excerpt['text'], '[audio')) || (!\str_starts_with($excerpt['text'], '[video') && !\str_starts_with($excerpt['text'], '[audio'))
|| (!($video = (\preg_match('/\[video.*src="([^"]*)".*\]/', $excerpt['text'], $matches) === 1)) || (!($video = (\preg_match('/\[video.*src="([^"]*)".*\]/', $excerpt['text'], $matches) === 1))
&& !($audio = (\preg_match('/\[audio.*src="([^"]*)".*\]/', $excerpt['text'], $matches) === 1))) && !($audio = (\preg_match('/\[audio.*src="([^"]*)".*\]/', $excerpt['text'], $matches) === 1)))
) { ) {
@ -2009,14 +2015,14 @@ class Markdown
/** /**
* Handle block list * Handle block list
* *
* @param array{body:string, indent:int, text:string} $line Line data * @param array{body:string, indent:int, text:string} $line Line data
* @param null|array $block Current block * @param null|array $current Current block
* *
* @return null|array * @return null|array
* *
* @since 1.0.0 * @since 1.0.0
*/ */
protected function blockList(array $line, ?array $block = null) : ?array protected function blockList(array $line, ?array $current = null) : ?array
{ {
if (!($this->options['lists'] ?? true)) { if (!($this->options['lists'] ?? true)) {
return null; return null;
@ -2039,6 +2045,17 @@ class Markdown
} }
$markerWithoutWhitespace = \strstr($matches[1], ' ', true); $markerWithoutWhitespace = \strstr($matches[1], ' ', true);
if ($markerWithoutWhitespace === false) {
$markerWithoutWhitespace = $matches[1];
}
if ($name === 'ul') {
$markerWithoutWhitespace = \substr($markerWithoutWhitespace, -1);
if ($markerWithoutWhitespace === false) {
$markerWithoutWhitespace = $matches[1];
}
}
$block = [ $block = [
'indent' => $line['indent'], 'indent' => $line['indent'],
@ -2046,7 +2063,7 @@ class Markdown
'data' => [ 'data' => [
'type' => $name, 'type' => $name,
'marker' => $matches[1], 'marker' => $matches[1],
'markerType' => ($name === 'ul' ? $markerWithoutWhitespace : \substr($markerWithoutWhitespace, -1)), 'markerType' => $markerWithoutWhitespace,
], ],
'element' => [ 'element' => [
'name' => $name, 'name' => $name,
@ -2057,12 +2074,17 @@ class Markdown
$block['data']['markerTypeRegex'] = \preg_quote($block['data']['markerType'], '/'); $block['data']['markerTypeRegex'] = \preg_quote($block['data']['markerType'], '/');
if ($name === 'ol') { if ($name === 'ol') {
$listStart = \ltrim(\strstr($matches[1], $block['data']['markerType'], true), '0') ?: '0'; $tmp = \strstr($matches[1], $block['data']['markerType'], true);
if ($tmp === false) {
$tmp = $matches[1];
}
$listStart = \ltrim($tmp, '0') ?: '0';
if ($listStart !== '1') { if ($listStart !== '1') {
if (isset($currentBlock) if (isset($current)
&& $currentBlock['type'] === 'Paragraph' && $current['type'] === 'Paragraph'
&& !isset($currentBlock['interrupted']) && !isset($current['interrupted'])
) { ) {
return null; return null;
} }
@ -2175,6 +2197,7 @@ class Markdown
} }
// Get the text of the heading // Get the text of the heading
$text = null;
if (isset($block['element']['handler']['argument'])) { if (isset($block['element']['handler']['argument'])) {
$text = $block['element']['handler']['argument']; $text = $block['element']['handler']['argument'];
} }
@ -2561,7 +2584,7 @@ class Markdown
return null; return null;
} }
$language = \trim(\preg_replace('/^`{3}([^\s]+)(.+)?/s', '$1', $line['text'])); $language = \trim(\preg_replace('/^`{3}([^\s]+)(.+)?/s', '$1', $line['text']) ?? '');
if (!($this->options['diagrams'] ?? true) if (!($this->options['diagrams'] ?? true)
|| !\in_array($language, ['mermaid', 'chart']) || !\in_array($language, ['mermaid', 'chart'])
@ -2621,7 +2644,6 @@ class Markdown
]; ];
} }
return null; return null;
} }
@ -2650,7 +2672,7 @@ class Markdown
return null; return null;
} }
$summary = \trim(\preg_replace('/^\?{3}(.+)?/s', '$1', $line['text'])); $summary = \trim(\preg_replace('/^\?{3}(.+)?/s', '$1', $line['text']) ?? '');
$infostring = \trim(\substr($line['text'], $openerLength), "\t "); $infostring = \trim(\substr($line['text'], $openerLength), "\t ");
if (\strpos($infostring, '?') !== false) { if (\strpos($infostring, '?') !== false) {
@ -2928,6 +2950,10 @@ class Markdown
$attributes = \preg_split('/[ ]+/', $attribute, - 1, \PREG_SPLIT_NO_EMPTY); $attributes = \preg_split('/[ ]+/', $attribute, - 1, \PREG_SPLIT_NO_EMPTY);
$classes = []; $classes = [];
if ($attributes === false) {
return [];
}
foreach ($attributes as $attribute) { foreach ($attributes as $attribute) {
if ($attribute[0] === '#') { if ($attribute[0] === '#') {
$data['id'] = \substr($attribute, 1); $data['id'] = \substr($attribute, 1);
@ -3083,10 +3109,10 @@ class Markdown
// Replace non-alphanumeric characters with our delimiter // Replace non-alphanumeric characters with our delimiter
$optionDelimiter = $this->options['toc']['delimiter'] ?? '-'; $optionDelimiter = $this->options['toc']['delimiter'] ?? '-';
$str = \preg_replace('/[^\p{L}\p{Nd}]+/u', $optionDelimiter, $str); $str = \preg_replace('/[^\p{L}\p{Nd}]+/u', $optionDelimiter, $str) ?? '';
// Remove duplicate delimiters // Remove duplicate delimiters
$str = \preg_replace('/(' . \preg_quote($optionDelimiter, '/') . '){2,}/', '$1', $str); $str = \preg_replace('/(' . \preg_quote($optionDelimiter, '/') . '){2,}/', '$1', $str) ?? '';
// Truncate slug to max. characters // Truncate slug to max. characters
$optionLimit = $this->options['toc']['limit'] ?? \mb_strlen($str, 'UTF-8'); $optionLimit = $this->options['toc']['limit'] ?? \mb_strlen($str, 'UTF-8');
@ -3164,7 +3190,7 @@ class Markdown
} }
$newStr .= '-' . $count; $newStr .= '-' . $count;
} while(isset($this->anchorDuplicates[$newStr])); } while (isset($this->anchorDuplicates[$newStr]));
$this->anchorDuplicates[$newStr] = 0; $this->anchorDuplicates[$newStr] = 0;
@ -3186,7 +3212,7 @@ class Markdown
if (!empty($this->options['headings']['blacklist']) && \is_array($this->options['headings']['blacklist'])) { if (!empty($this->options['headings']['blacklist']) && \is_array($this->options['headings']['blacklist'])) {
foreach ($this->options['headings']['blacklist'] as $v) { foreach ($this->options['headings']['blacklist'] as $v) {
$this->anchorDuplicates[$v] = 0; $this->anchorDuplicates[(string) $v] = 0;
} }
} }
@ -3608,7 +3634,7 @@ class Markdown
], ],
]; ];
\uasort($this->definitionData['Footnote'], 'self::sortFootnotes'); \uasort($this->definitionData['Footnote'], ['self', 'sortFootnotes']);
foreach ($this->definitionData['Footnote'] as $definitionId => $definitionData) { foreach ($this->definitionData['Footnote'] as $definitionId => $definitionData) {
if (!isset($definitionData['number'])) { if (!isset($definitionData['number'])) {
@ -3703,8 +3729,18 @@ class Markdown
// http://stackoverflow.com/q/4879946/200145 // http://stackoverflow.com/q/4879946/200145
$dom->loadHTML($elementMarkup); $dom->loadHTML($elementMarkup);
$dom->removeChild($dom->doctype);
$dom->replaceChild($dom->firstChild->firstChild->firstChild, $dom->firstChild); if ($dom->documentElement === null) {
return '';
}
if ($dom->doctype !== null) {
$dom->removeChild($dom->doctype);
}
if ($dom->firstChild !== null && $dom->firstChild->firstChild?->firstChild !== null) {
$dom->replaceChild($dom->firstChild->firstChild->firstChild, $dom->firstChild);
}
$elementText = ''; $elementText = '';
@ -3719,6 +3755,10 @@ class Markdown
} else { } else {
foreach ($dom->documentElement->childNodes as $node) { foreach ($dom->documentElement->childNodes as $node) {
$nodeMarkup = $dom->saveHTML($node); $nodeMarkup = $dom->saveHTML($node);
if ($nodeMarkup === false) {
$nodeMarkup = '';
}
$elementText .= $node instanceof \DOMElement && !\in_array($node->nodeName, $this->textLevelElements) $elementText .= $node instanceof \DOMElement && !\in_array($node->nodeName, $this->textLevelElements)
? $this->processTag($nodeMarkup) ? $this->processTag($nodeMarkup)
: $nodeMarkup; : $nodeMarkup;
@ -3729,6 +3769,9 @@ class Markdown
$dom->documentElement->nodeValue = 'placeholder\x1A'; $dom->documentElement->nodeValue = 'placeholder\x1A';
$markup = $dom->saveHTML($dom->documentElement); $markup = $dom->saveHTML($dom->documentElement);
if ($markup === false) {
return '';
}
return \str_replace('placeholder\x1A', $elementText, $markup); return \str_replace('placeholder\x1A', $elementText, $markup);
} }
@ -4504,7 +4547,7 @@ class Markdown
* *
* @param array $element Element to render * @param array $element Element to render
* *
* @return : string * @return string
* *
* @since 1.0.0 * @since 1.0.0
*/ */
@ -4534,6 +4577,7 @@ class Markdown
} }
$permitRawHtml = false; $permitRawHtml = false;
$text = null;
if (isset($element['text'])) { if (isset($element['text'])) {
$text = $element['text']; $text = $element['text'];

View File

@ -82,7 +82,7 @@ final class SpreadsheetParser
/** @var \phpOMS\Utils\Parser\Spreadsheet\SpreadsheetWriter $writer */ /** @var \phpOMS\Utils\Parser\Spreadsheet\SpreadsheetWriter $writer */
$writer = IOFactory::createWriter($spreadsheet, 'custom'); $writer = IOFactory::createWriter($spreadsheet, 'custom');
$html = $writer->generateHtmlAll(); $html = $writer->generateHtmlAll();
$doc = new \DOMDocument(); $doc = new \DOMDocument();
$html = \preg_replace( $html = \preg_replace(

View File

@ -45,25 +45,38 @@ final class XmlParser
*/ */
public static function parseXml(string $path, string $output = 'xml', string $xpath = '') : string public static function parseXml(string $path, string $output = 'xml', string $xpath = '') : string
{ {
$doc = new \DOMDocument(); $doc = new \DOMDocument();
$doc->preserveWhiteSpace = true; $doc->preserveWhiteSpace = true;
$doc->formatOutput = true; $doc->formatOutput = true;
$xml = \file_get_contents($path); $xml = \file_get_contents($path);
if ($xml === false || $xml === null) {
return '';
}
$xml = \preg_replace( $xml = \preg_replace(
['~<style.*?</style>~', '~<script.*?</script>~'], ['~<style.*?</style>~', '~<script.*?</script>~'],
['', ''], ['', ''],
$xml $xml
); );
$doc->loadXML($path); if ($xml === null) {
return '';
if (empty($xpath)) {
return $doc->loadXML($xml);
} }
$content = ''; $result = $doc->loadXML($xml);
$xNode = new \DOMXpath($doc); if ($result === false) {
return '';
}
if (empty($xpath)) {
$result = $doc->saveHTML();
return $result === false ? '' : $result;
}
$content = '';
$xNode = new \DOMXpath($doc);
$elements = $xNode->query($xpath); $elements = $xNode->query($xpath);
if ($elements === false) { if ($elements === false) {

View File

@ -24,6 +24,16 @@ namespace phpOMS\Utils\RnG;
*/ */
final class Number final class Number
{ {
/**
* Generate normal distributed random number
*
* @param int $min Min value
* @param int $max Max value
*
* @return int
*
* @since 1.0.0
*/
public static function normalDistributedRand(int $min, int $max) : int public static function normalDistributedRand(int $min, int $max) : int
{ {
$u1 = \mt_rand(1, 100) / 100; $u1 = \mt_rand(1, 100) / 100;
@ -36,7 +46,17 @@ final class Number
} }
/** /**
* Generate exponentially distributed random number
*
* For values [0; 100] a lambda of around 0.2 is recommended * For values [0; 100] a lambda of around 0.2 is recommended
*
* @param int $min Min value
* @param int $max Max value
* @param float $lambda Lambda
*
* @return int
*
* @since 1.0.0
*/ */
public static function exponentialDistributedRand(int $min, int $max, float $lambda) : int public static function exponentialDistributedRand(int $min, int $max, float $lambda) : int
{ {

View File

@ -79,7 +79,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
$mapper->insert(); $mapper->insert();
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_1.*')->from('insert_1')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_1.*')->from('insert_1')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],
@ -91,7 +91,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
); );
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_2.*')->from('insert_2')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_2.*')->from('insert_2')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],
@ -114,7 +114,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
$mapper->insert(); $mapper->insert();
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_1.*')->from('insert_1')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_1.*')->from('insert_1')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],
@ -126,7 +126,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
); );
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_2.*')->from('insert_2')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_2.*')->from('insert_2')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],
@ -149,7 +149,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
$mapper->insert(); $mapper->insert();
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_1.*')->from('insert_1')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_1.*')->from('insert_1')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],
@ -161,7 +161,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
); );
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_2.*')->from('insert_2')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_2.*')->from('insert_2')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],
@ -184,7 +184,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
$mapper->insert(); $mapper->insert();
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_1.*')->from('insert_1')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_1.*')->from('insert_1')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],
@ -196,7 +196,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
); );
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_2.*')->from('insert_2')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_2.*')->from('insert_2')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],
@ -211,7 +211,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
$mapper->update(); $mapper->update();
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_1.*')->from('insert_1')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_1.*')->from('insert_1')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],
@ -223,7 +223,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
); );
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_2.*')->from('insert_2')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_2.*')->from('insert_2')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],
@ -246,7 +246,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
$mapper->insert(); $mapper->insert();
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_1.*')->from('insert_1')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_1.*')->from('insert_1')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],
@ -258,7 +258,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
); );
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_2.*')->from('insert_2')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_2.*')->from('insert_2')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],
@ -273,7 +273,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
$mapper->update(); $mapper->update();
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_1.*')->from('insert_1')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_1.*')->from('insert_1')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],
@ -285,7 +285,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
); );
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_2.*')->from('insert_2')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_2.*')->from('insert_2')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],
@ -308,7 +308,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
$mapper->insert(); $mapper->insert();
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_1.*')->from('insert_1')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_1.*')->from('insert_1')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],
@ -320,7 +320,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
); );
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_2.*')->from('insert_2')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_2.*')->from('insert_2')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],
@ -335,7 +335,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
$mapper->update(); $mapper->update();
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_1.*')->from('insert_1')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_1.*')->from('insert_1')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],
@ -347,7 +347,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
); );
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_2.*')->from('insert_2')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_2.*')->from('insert_2')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],
@ -374,7 +374,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
$mapper->insert(); $mapper->insert();
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_1.*')->from('insert_1')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_1.*')->from('insert_1')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],
@ -386,7 +386,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
); );
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_2.*')->from('insert_2')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_2.*')->from('insert_2')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],
@ -426,7 +426,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
$mapper->insert(); $mapper->insert();
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_1.*')->from('insert_1')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_1.*')->from('insert_1')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],
@ -438,7 +438,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
); );
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_2.*')->from('insert_2')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_2.*')->from('insert_2')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],
@ -478,7 +478,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
$mapper->insert(); $mapper->insert();
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_1.*')->from('insert_1')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_1.*')->from('insert_1')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],
@ -490,7 +490,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
); );
$builder = new Builder($this->sqlite, true); $builder = new Builder($this->sqlite, true);
$data = $builder->select('insert_2.*')->from('insert_2')->execute()->fetchAll(\PDO::FETCH_ASSOC); $data = $builder->select('insert_2.*')->from('insert_2')->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? [];
self::assertEquals( self::assertEquals(
[ [
['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'], ['id' => 1, 'int' => 2, 'decimal' => 2.0, 'bool' => 1, 'varchar' => 'Line 1', 'datetime' => '43631'],

View File

@ -22,7 +22,6 @@ use phpOMS\Localization\L11nManager;
use phpOMS\Localization\Localization; use phpOMS\Localization\Localization;
use phpOMS\Message\Http\HttpRequest; use phpOMS\Message\Http\HttpRequest;
use phpOMS\Message\Http\HttpResponse; use phpOMS\Message\Http\HttpResponse;
use phpOMS\Uri\HttpUri;
use phpOMS\Views\View; use phpOMS\Views\View;
use phpOMS\Views\ViewAbstract; use phpOMS\Views\ViewAbstract;