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) {
$split = \explode('.', $image);
$split = \explode('.', $image);
$extension = \end($split);
SystemUtils::runProc(

View File

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

View File

@ -119,4 +119,36 @@ final class AgglomerativeClustering implements ClusteringInterface
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
{
/**
* {@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
{
/**
* {@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) {
$c = $this->cluster($point);
$this->clusters[$c] = $point;
$c = $this->cluster($point);
$this->clusters[$c?->name] = $point;
}
return $this->clusters;

View File

@ -27,4 +27,35 @@ namespace phpOMS\Algorithm\Clustering;
*/
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
{
/**
* Id
*
* @var int
* @since 1.0.0
*/
public int $id = 0;
/**
* Time of the execution
*
* @var int
* @since 1.0.0
*/
public int $executionTime = 0;
/**
* Priority.
*
* @var float
* @since 1.0.0
*/
public float $priority = 0.0;
/**
* Value this job generates.
*
* @var float
* @since 1.0.0
*/
public float $value = 0.0;
/**
* Cost of executing this job.
*
* @var float
* @since 1.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;
/** 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;
/**
* What is the deadline for this job?
*
* @param \DateTime
* @since 1.0.0
*/
public \DateTime $deadline;
/**
* Which steps must be taken during the job execution
*
* @var JobStep[]
* @since 1.0.0
*/
public array $steps = [];
/**
* Constructor.
*
* @since 1.0.0
*/
public function __construct()
{
$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;
}

View File

@ -24,8 +24,24 @@ namespace phpOMS\Scheduling;
*/
final class ScheduleQueue
{
/**
* Queue
*
* @var Job[]
* @since 1.0.0
*/
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
{
$jobs = [];
@ -103,11 +119,33 @@ final class ScheduleQueue
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
{
$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
{
$jobs = $this->get($size, $type);
@ -118,6 +156,15 @@ final class ScheduleQueue
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
{
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
{
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]);
}

View File

@ -22,8 +22,8 @@ use phpOMS\Math\Stochastic\Distribution\NormalDistribution;
* @package phpOMS\Algorithm\Rating
* @license OMS License 2.0
* @link https://jingga.app
* @since 1.0.0
* @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
* https://github.com/Karaka-Management/phpOMS/issues/337
@ -50,6 +50,17 @@ class TrueSkill
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(
?float $mu = null,
?float $sigma = null,
@ -64,7 +75,18 @@ class TrueSkill
$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;
$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));
}
/**
*
*/
private function buildRatingLayer() : void
{
}
/**
*
*/
private function buildPerformanceLayer() : void
{
}
/**
*
*/
private function buildTeamPerformanceLayer() : void
{
}
/**
*
*/
private function buildTruncLayer() : void
{
}
/**
*
*/
private function factorGraphBuilders()
{
// Rating layer
@ -238,6 +275,9 @@ class TrueSkill
];
}
/**
*
*/
public function rating() : void
{
// 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) {
case ShippingType::DHL:
return new \phpOMS\Api\Shipping\DHL\DHLShipping();
return new \phpOMS\Api\Shipping\DHL\DHLInternationalShipping();
case ShippingType::DPD:
return new \phpOMS\Api\Shipping\DPD\DPDShipping();
case ShippingType::FEDEX:

View File

@ -37,9 +37,15 @@ final class BayesianPersonalizedRanking
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.
// regularization prevents over-fitting by adding a penalty for large parameter values.
/**
* Constructor.
*
* @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)
{
$this->numFactors = $numFactors;
@ -47,7 +53,14 @@ final class BayesianPersonalizedRanking
$this->regularization = $regularization;
}
private function generateRandomFactors()
/**
* Calculate random factors
*
* @return array
*
* @since 1.0.0
*/
private function generateRandomFactors() : array
{
$factors = [];
for ($i = 0; $i < $this->numFactors; ++$i) {
@ -57,6 +70,9 @@ final class BayesianPersonalizedRanking
return $factors;
}
/**
* @todo implement
*/
public function predict($userId, $itemId) {
$userFactor = $this->userFactors[$userId];
$itemFactor = $this->itemFactors[$itemId];
@ -69,6 +85,9 @@ final class BayesianPersonalizedRanking
return $score;
}
/**
* @todo implement
*/
public function updateFactors($userId, $posItemId, $negItemId) : void
{
if (!isset($this->userFactors[$userId])) {

View File

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

View File

@ -158,8 +158,23 @@ abstract class DataMapperAbstract
$this->db = $db;
}
/**
* Column name of the index
*
* @var string
* @since 1.0.0
*/
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
{
$this->indexedBy = $index;

View File

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

View File

@ -748,9 +748,9 @@ final class ReadMapper extends DataMapperAbstract
}
/** @var self $relMapper */
$relMapper = $this->createRelationMapper($rel['mapper']::reader(db: $this->db), $member);
$relMapper->depth = $this->depth + 1;
$relMapper->type = $this->type;
$relMapper = $this->createRelationMapper($rel['mapper']::reader(db: $this->db), $member);
$relMapper->depth = $this->depth + 1;
$relMapper->type = $this->type;
$relMapper->joinAlias = '_' . $member;
// 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
/** @var self $belongsToMapper */
$belongsToMapper = $this->createRelationMapper($mapper::get($this->db), $member);
$belongsToMapper->depth = $this->depth + 1;
$belongsToMapper = $this->createRelationMapper($mapper::get($this->db), $member);
$belongsToMapper->depth = $this->depth + 1;
$belongsToMapper->joinAlias = '_' . $member;
$belongsToMapper->where(

View File

@ -181,7 +181,7 @@ final class UpdateMapper extends DataMapperAbstract
\usleep(10000);
$repeat = true;
}
} while($repeat);
} while ($repeat);
} catch (\Throwable $t) {
// @codeCoverageIgnoreStart
\phpOMS\Log\FileLogger::getInstance()->error(
@ -210,19 +210,48 @@ final class UpdateMapper extends DataMapperAbstract
/** @var class-string<DataMapperFactory> $mapper */
$mapper = $this->mapper::BELONGS_TO[$propertyName]['mapper'];
if (!isset($this->with[$propertyName])) {
$id = $mapper::getObjectId($obj);
if (isset($this->with[$propertyName])) {
/** @var self $relMapper */
$relMapper = $this->createRelationMapper($mapper::update(db: $this->db), $propertyName);
$relMapper->depth = $this->depth + 1;
return empty($id) && $mapper::isNullModel($obj)
? null
: $id;
$id = $relMapper->execute($obj);
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 */
$relMapper = $this->createRelationMapper($mapper::update(db: $this->db), $propertyName);
$relMapper->depth = $this->depth + 1;
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)
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 */
$mapper = $this->mapper::OWNS_ONE[$propertyName]['mapper'];
if (!isset($this->with[$propertyName])) {
$id = $mapper::getObjectId($obj);
if (isset($this->with[$propertyName])) {
/** @var self $relMapper */
$relMapper = $this->createRelationMapper($mapper::update(db: $this->db), $propertyName);
$relMapper->depth = $this->depth + 1;
return empty($id) && $mapper::isNullModel($obj)
? null
: $id;
$id = $relMapper->execute($obj);
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 */
$relMapper = $this->createRelationMapper($mapper::update(db: $this->db), $propertyName);
$relMapper->depth = $this->depth + 1;
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']};
}
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);
$repeat = true;
}
} while($repeat);
} while ($repeat);
$objId = empty($id = $this->mapper::getObjectId($obj)) ? $this->db->con->lastInsertId() : $id;
\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
{
if (!\is_object($obj)) {
return $obj;
// @question This code prevents us from EVER creating an object with a 'by' reference since we always assume
// 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 */
$mapper = $this->mapper::OWNS_ONE[$propertyName]['mapper'];
$primaryKey = $mapper::getObjectId($obj);
if (empty($primaryKey)) {
return $mapper::create(db: $this->db)->execute($obj);
}
return $primaryKey;
// @bug The $mapper::create() might cause a problem if 'by' is set.
// 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)
: $primaryKey;
}
/**
@ -237,13 +253,11 @@ final class WriteMapper extends DataMapperAbstract
*/
private function createBelongsTo(string $propertyName, object $obj) : mixed
{
if (!\is_object($obj)) {
return $obj;
}
$mapper = '';
$primaryKey = 0;
// @question This code prevents us from EVER creating an object with a 'by' reference since we always assume
// 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::BELONGS_TO[$propertyName]['by'])) {
// 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) {
@ -253,6 +267,10 @@ final class WriteMapper extends DataMapperAbstract
} else {
$obj = $obj->{$this->mapper::BELONGS_TO[$propertyName]['by']};
}
if (!\is_object($obj)) {
return $obj;
}
}
/** @var class-string<DataMapperFactory> $mapper */
@ -435,7 +453,7 @@ final class WriteMapper extends DataMapperAbstract
\usleep(10000);
$repeat = true;
}
} while($repeat);
} while ($repeat);
} catch (\Throwable $t) {
// @codeCoverageIgnoreStart
\phpOMS\Log\FileLogger::getInstance()->error(

View File

@ -1296,7 +1296,7 @@ class Builder extends BuilderAbstract
*
* @param string|array $columns Columns to join on
* @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 null|string $table Table this belongs to
*

View File

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

View File

@ -43,10 +43,21 @@ class Concat extends Builder
$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
{
$this->delim = $delim;
$this->as = $as;
$this->as = $as;
$this->select($columns);
}
@ -64,4 +75,4 @@ class Concat extends Builder
return $query;
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -196,6 +196,16 @@ final class Functions
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
{
return $a - ((int) ($a / $b)) * $b;

View File

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

View File

@ -20,7 +20,6 @@ namespace phpOMS\Message\Mail;
use phpOMS\Security\Guard;
use phpOMS\System\SystemUtils;
use phpOMS\Utils\StringUtils;
use phpOMS\Validation\Network\Email as EmailValidator;
use phpOMS\Validation\Network\Hostname;
@ -398,7 +397,6 @@ class MailHandler
*
* @param string $to To
* @param Email $mail Mail
* @param string $body Message Body
* @param string $header Additional Header(s)
* @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.
*
* @param mixed $key Response id
* @param mixed $response Response to add
* @param mixed $key Response id
* @param mixed $response Response to add
*
* @return void
*

View File

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

View File

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

View File

@ -50,9 +50,11 @@ abstract class 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
*/

View File

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

View File

@ -372,9 +372,9 @@ class SmartDateTime extends \DateTime
*
* @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
*/
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
*/
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
*/
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;
}
/**
* 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
{
$days = \floor($duration / (24 * 3600));

View File

@ -407,7 +407,11 @@ final class HttpUri implements UriInterface
if (empty($this->fragment)) {
$this->uri .= $toAdd;
} 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();
$html = \file_get_contents($path);
if ($html === false) {
return '';
}
$html = \preg_replace(
['~<style.*?</style>~', '~<script.*?</script>~'],
['', ''],
@ -63,8 +67,8 @@ final class HtmlParser
return empty($node->textContent) ? '' : $node->textContent;
}
$content = '';
$xNode = new \DOMXpath($doc);
$content = '';
$xNode = new \DOMXpath($doc);
$elements = $xNode->query($xpath);
if ($elements === false) {

View File

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

View File

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

View File

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

View File

@ -24,6 +24,16 @@ namespace phpOMS\Utils\RnG;
*/
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
{
$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
*
* @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
{

View File

@ -79,7 +79,7 @@ final class SpreadsheetDatabaseMapperTest extends \PHPUnit\Framework\TestCase
$mapper->insert();
$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(
[
['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);
$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(
[
['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();
$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(
[
['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);
$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(
[
['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();
$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(
[
['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);
$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(
[
['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();
$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(
[
['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);
$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(
[
['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();
$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(
[
['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);
$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(
[
['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();
$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(
[
['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);
$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(
[
['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();
$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(
[
['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);
$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(
[
['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();
$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(
[
['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);
$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(
[
['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();
$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(
[
['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);
$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(
[
['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();
$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(
[
['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);
$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(
[
['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();
$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(
[
['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);
$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(
[
['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();
$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(
[
['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);
$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(
[
['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\Message\Http\HttpRequest;
use phpOMS\Message\Http\HttpResponse;
use phpOMS\Uri\HttpUri;
use phpOMS\Views\View;
use phpOMS\Views\ViewAbstract;