diff --git a/Account/PermissionAbstract.php b/Account/PermissionAbstract.php index b970c2c89..abe242cb1 100755 --- a/Account/PermissionAbstract.php +++ b/Account/PermissionAbstract.php @@ -46,10 +46,10 @@ class PermissionAbstract implements \JsonSerializable /** * App name. * - * @var null|string + * @var null|int * @since 1.0.0 */ - protected ?string $app = null; + protected ?int $app = null; /** * Module id. @@ -135,7 +135,7 @@ class PermissionAbstract implements \JsonSerializable * Constructor. * * @param null|int $unit Unit to check (null if all are acceptable) - * @param null|string $app App to check (null if all are acceptable) + * @param null|int $app App to check (null if all are acceptable) * @param null|string $module Module Module to check (null if all are acceptable) * @param null|string $from Provided by which module * @param null|int $category Category (e.g. customer) (null if all are acceptable) @@ -147,7 +147,7 @@ class PermissionAbstract implements \JsonSerializable */ public function __construct( int $unit = null, - string $app = null, + int $app = null, string $module = null, string $from = null, int $category = null, @@ -211,11 +211,11 @@ class PermissionAbstract implements \JsonSerializable /** * Get app name. * - * @return null|string + * @return null|int * * @since 1.0.0 */ - public function getApp() : ?string + public function getApp() : ?int { return $this->app; } @@ -223,13 +223,13 @@ class PermissionAbstract implements \JsonSerializable /** * Set app name. * - * @param string $app App name + * @param int $app App name * * @return void * * @since 1.0.0 */ - public function setApp(string $app = null) : void + public function setApp(int $app = null) : void { $this->app = $app; } @@ -467,7 +467,7 @@ class PermissionAbstract implements \JsonSerializable * * @param int $permission Permission to check * @param null|int $unit Unit Unit to check (null if all are acceptable) - * @param null|string $app App App to check (null if all are acceptable) + * @param null|int $app App App to check (null if all are acceptable) * @param null|string $module Module Module to check (null if all are acceptable) * @param null|int $category Category (e.g. customer) (null if all are acceptable) * @param null|int $element (e.g. customer id) (null if all are acceptable) @@ -480,7 +480,7 @@ class PermissionAbstract implements \JsonSerializable public function hasPermission( int $permission, int $unit = null, - string $app = null, + int $app = null, string $module = null, int $category = null, int $element = null, diff --git a/Account/PermissionHandlingTrait.php b/Account/PermissionHandlingTrait.php index 2d8846802..b445b7859 100755 --- a/Account/PermissionHandlingTrait.php +++ b/Account/PermissionHandlingTrait.php @@ -123,7 +123,7 @@ trait PermissionHandlingTrait * * @param int $permission Permission to check * @param null|int $unit Unit Unit to check (null if all are acceptable) - * @param null|string $app App App to check (null if all are acceptable) + * @param null|int $app App App to check (null if all are acceptable) * @param null|string $module Module Module to check (null if all are acceptable) * @param null|int $category Type (e.g. customer) (null if all are acceptable) * @param null|int $element (e.g. customer id) (null if all are acceptable) @@ -136,15 +136,13 @@ trait PermissionHandlingTrait public function hasPermission( int $permission, int $unit = null, - string $app = null, + int $app = null, string $module = null, int $category = null, int $element = null, int $component = null ) : bool { - $app = $app !== null ? \strtolower($app) : $app; - foreach ($this->permissions as $p) { if ($p->hasPermission($permission, $unit, $app, $module, $category, $element, $component)) { return true; diff --git a/Ai/Ocr/BasicOcr.php b/Ai/Ocr/BasicOcr.php index f98f72a32..632513167 100755 --- a/Ai/Ocr/BasicOcr.php +++ b/Ai/Ocr/BasicOcr.php @@ -70,7 +70,7 @@ final class BasicOcr } /** - * Reat image from path + * Read image from path * * @param string $path Image to read * @param int $limit Limit @@ -93,7 +93,8 @@ final class BasicOcr if (($read = \fread($fp, 4)) === false || ($unpack = \unpack('N', $read)) === false) { return []; // @codeCoverageIgnore } - $magicNumber = $unpack[1]; + + // $magicNumber = $unpack[1]; if (($read = \fread($fp, 4)) === false || ($unpack = \unpack('N', $read)) === false) { return []; // @codeCoverageIgnore @@ -125,6 +126,7 @@ final class BasicOcr ) { return []; // @codeCoverageIgnore } + $images[] = \array_values($unpack); } @@ -237,6 +239,53 @@ final class BasicOcr return $dist; } + public static function imagesToMNIST(array $images, string $out, int $resolution) : void + { + $out = \fopen($out, 'wb'); + + \fwrite($out, \pack('N', 2051)); + \fwrite($out, \pack('N', 1)); + \fwrite($out, \pack('N', $resolution)); + \fwrite($out, \pack('N', $resolution)); + + foreach ($images as $in) { + $im = \imagecreatefromstring(\file_get_contents($in)); + $new = \imagescale($im, $resolution, $resolution); + + // Convert the image to grayscale and normalize the pixel values + $mnist = []; + for ($i = 0; $i < $resolution; ++$i) { + for ($j = 0; $j < $resolution; ++$j) { + $pixel = \imagecolorat($new, $j, $i); + $gray = \round((0.299 * (($pixel >> 16) & 0xFF) + 0.587 * (($pixel >> 8) & 0xFF) + 0.114 * ($pixel & 0xFF)) / 255, 3); + + \array_push($mnist, $gray); + } + } + + for ($i = 0; $i < \count($mnist); $i++) { + \fwrite($out, \pack('C', \round($mnist[$i] * 255))); + } + } + + \fclose($out); + } + + public static function labelsToMNIST(array $data, string $out) : void + { + // Only allows single char labels + $out = \fopen($out, 'wb'); + + \fwrite($out, \pack('N', 2049)); + \fwrite($out, \pack('N', 1)); + + foreach ($data as $e) { + \fwrite($out, \pack('C', $e)); + } + + \fclose($out); + } + /** * Categorize an unknown image * @@ -250,7 +299,6 @@ final class BasicOcr */ public function matchImage(string $path, int $comparison = 3, int $limit = 0) : array { - // @todo: implement image reading if it isn't an mnist file $Xtest = $this->readImages($path, $limit); return $this->kNearest($this->Xtrain, $this->ytrain, $Xtest, $comparison); diff --git a/Ai/Ocr/Tesseract/TesseractOcr.php b/Ai/Ocr/Tesseract/TesseractOcr.php index a25a43c46..87a2f1e26 100755 --- a/Ai/Ocr/Tesseract/TesseractOcr.php +++ b/Ai/Ocr/Tesseract/TesseractOcr.php @@ -115,8 +115,6 @@ final class TesseractOcr return ''; } - // @todo: auto flip image if x% of text are garbage words? - \unlink($filepath); return \trim($parsed); diff --git a/Algorithm/Graph/DependencyResolver.php b/Algorithm/Graph/DependencyResolver.php index c9cd1181e..21f570043 100755 --- a/Algorithm/Graph/DependencyResolver.php +++ b/Algorithm/Graph/DependencyResolver.php @@ -37,7 +37,7 @@ final class DependencyResolver { $resolved = []; $unresolved = []; - foreach ($graph as $table => $dependency) { + foreach ($graph as $table => $_) { self::dependencyResolve($table, $graph, $resolved, $unresolved); } diff --git a/Api/EUVAT/EUVATBffOnline.php b/Api/EUVAT/EUVATBffOnline.php index d104b1f4e..638522252 100644 --- a/Api/EUVAT/EUVATBffOnline.php +++ b/Api/EUVAT/EUVATBffOnline.php @@ -48,18 +48,18 @@ final class EUVATBffOnline implements EUVATInterface $request->setMethod(RequestMethod::GET); $result = [ - 'status' => -1, - 'vat' => 'C', - 'name' => '', - 'city' => '', - 'postal' => '', + 'status' => -1, + 'vat' => 'C', + 'name' => '', + 'city' => '', + 'postal' => '', 'address' => '', - 'body' => '', + 'body' => '', ]; $matches = []; try { - $body = Rest::request($request)->getBody(); + $body = Rest::request($request)->getBody(); $result['body'] = $body; \preg_match('/ErrorCode.*?(\d+)/s', $body, $matches); @@ -93,13 +93,13 @@ final class EUVATBffOnline implements EUVATInterface ) : array { $result = [ - 'status' => -1, - 'vat' => 'C', - 'name' => 'C', - 'city' => 'C', - 'postal' => 'C', + 'status' => -1, + 'vat' => 'C', + 'name' => 'C', + 'city' => 'C', + 'postal' => 'C', 'address' => 'C', - 'body' => '', + 'body' => '', ]; if (empty($ownVAT)) { @@ -112,7 +112,7 @@ final class EUVATBffOnline implements EUVATInterface $request->setMethod(RequestMethod::GET); try { - $body = Rest::request($request)->getBody(); + $body = Rest::request($request)->getBody(); $result['body'] = $body; $matches = []; diff --git a/Api/EUVAT/EUVATVies.php b/Api/EUVAT/EUVATVies.php index 6a46dc24e..f29fc3866 100644 --- a/Api/EUVAT/EUVATVies.php +++ b/Api/EUVAT/EUVATVies.php @@ -68,6 +68,11 @@ final class EUVATVies implements EUVATInterface $result['body'] = $body; $json = \json_decode($body, true); + + if ($json === false) { + return $result; + } + $result = \array_merge($result, self::parseResponse($json)); $result['status'] = $json['userError'] === 'VALID' ? 0 : -1; @@ -91,13 +96,13 @@ final class EUVATVies implements EUVATInterface ) : array { $result = [ - 'status' => -1, - 'vat' => 'C', - 'name' => 'C', - 'city' => 'C', - 'postal' => 'C', + 'status' => -1, + 'vat' => 'C', + 'name' => 'C', + 'city' => 'C', + 'postal' => 'C', 'address' => 'C', - 'body' => '', + 'body' => '', ]; $request = new HttpRequest( @@ -110,10 +115,15 @@ final class EUVATVies implements EUVATInterface $request->setMethod(RequestMethod::GET); try { - $body = Rest::request($request)->getBody(); + $body = Rest::request($request)->getBody(); $result['body'] = $body; $json = \json_decode($body, true); + + if ($json === false) { + return $result; + } + $result = \array_merge($result, self::parseResponse($json)); if ($otherName === '') { @@ -167,14 +177,14 @@ final class EUVATVies implements EUVATInterface private static function parseResponse(array $json) : array { $result = [ - 'vat' => '', - 'name' => '', - 'city' => '', - 'postal' => '', + 'vat' => '', + 'name' => '', + 'city' => '', + 'postal' => '', 'address' => '', ]; - $result['vat'] = $json['isValid'] ? 'A' : 'B'; + $result['vat'] = $json['isValid'] ? 'A' : 'B'; $result['name'] = $json['isValid']; $result['city'] = \stripos($json['address'], "\n") !== false @@ -194,9 +204,9 @@ final class EUVATVies implements EUVATInterface : $json['address']; - $result['name'] = $result['name'] === '---' ? '' : $result['name']; - $result['city'] = $result['city'] === '---' ? '' : $result['city']; - $result['postal'] = $result['postal'] === '---' ? '' : $result['postal']; + $result['name'] = $result['name'] === '---' ? '' : $result['name']; + $result['city'] = $result['city'] === '---' ? '' : $result['city']; + $result['postal'] = $result['postal'] === '---' ? '' : $result['postal']; $result['address'] = $result['address'] === '---' ? '' : $result['address']; return $result; diff --git a/Business/Finance/Forensics.php b/Business/Finance/Forensics.php index 3885a0d47..6a589eb15 100644 --- a/Business/Finance/Forensics.php +++ b/Business/Finance/Forensics.php @@ -34,7 +34,16 @@ final class Forensics { } - public static function benfordAnalysis(array $data) + /** + * Perform the Benford analysis + * + * @param array $data Data to analyze + * + * @return array + * + * @since 1.0.0 + */ + public static function benfordAnalysis(array $data) : array { $digits = \array_fill(1, 9, 0); $size = \count($data); @@ -52,6 +61,13 @@ final class Forensics return $results; } + /** + * Calculate the general Benford distribution + * + * @return array + * + * @since 1.0.0 + */ public static function expectedBenfordDistribution() : array { $expected = []; diff --git a/DataStorage/Session/HttpSession.php b/DataStorage/Session/HttpSession.php index 4c07f82c8..d10141cf3 100755 --- a/DataStorage/Session/HttpSession.php +++ b/DataStorage/Session/HttpSession.php @@ -96,6 +96,8 @@ final class HttpSession implements SessionInterface ]); \session_start(); // @codeCoverageIgnoreEnd + } else { + throw new \Exception('Bad application workflow'); } if ($this->inactivityInterval > 0 diff --git a/Image/Kernel.php b/Image/Kernel.php index 9a829a283..50347fbdf 100755 --- a/Image/Kernel.php +++ b/Image/Kernel.php @@ -93,28 +93,42 @@ final class Kernel if (\count($kernel) === 3) { \imageconvolution($im, $kernel, 1, 0); } else { - // @todo: implement @see https://rosettacode.org/wiki/Image_convolution - // @todo: not working yet $dim = [\imagesx($im), \imagesy($im)]; - $kDim = [\count($kernel[1]), \count($kernel)]; // @todo: is the order correct? mhh... + $kDim = [\count($kernel[1]), \count($kernel)]; $kWidthRadius = NumericUtils::uRightShift($kDim[0], 1); $kHeightRadius = NumericUtils::uRightShift($kDim[1], 1); - for ($i = $dim[0] - 1; $i >= 0; --$i) { - for ($j = $dim[1] - 1; $j >= 0; --$j) { - $newPixel = 0; + for ($y = 0; $y < $dim[1]; ++$y) { + for ($x = 0; $x < $dim[0]; ++$x) { + $newR = 0; + $newG = 0; + $newB = 0; - for ($ki = $kDim[0] - 1; $ki >= 0; --$ki) { - for ($kj = $kDim[1] - 1; $kj >= 0; --$kj) { - $newPixel += $kernel[$ki][$kj] * \imagecolorat($im, - \min(\max($i + $ki - $kWidthRadius, 0), $dim[0] - 1), - \min(\max($j + $kj - $kHeightRadius, 0), $dim[1] - 1) + for ($ky = 0; $ky < $kDim[0]; ++$ky) { + for ($kx = 0; $kx < $kDim[1]; ++$kx) { + $pixel = \imagecolorat($im, + \min(\max($x + $kx - $kWidthRadius, 0), $dim[0] - 1), + \min(\max($y + $ky - $kHeightRadius, 0), $dim[1] - 1) ); + + // old + $r = ($pixel >> 16) & 0xFF; + $g = ($pixel >> 8) & 0xFF; + $b = $pixel & 0xFF; + + // new + $newR += $r * $kernel[$ky][$kx]; + $newG += $g * $kernel[$ky][$kx]; + $newB += $b * $kernel[$ky][$kx]; } } - \imagesetpixel($im, $i, $j, (int) $newPixel); + $newR = \max(0, \min(255, $newR)); + $newG = \max(0, \min(255, $newG)); + $newB = \max(0, \min(255, $newB)); + + \imagesetpixel($im, $x, $y, (int) (($newR << 16) + ($newG << 8) | $newB)); } } } diff --git a/Localization/ISO639CountryTrait.php b/Localization/ISO639CountryTrait.php new file mode 100644 index 000000000..e065f844f --- /dev/null +++ b/Localization/ISO639CountryTrait.php @@ -0,0 +1,542 @@ + language trait. + * + * @package phpOMS\Localization + * @license OMS License 2.0 + * @link https://jingga.app + * @since 1.0.0 + */ +trait ISO639CountryTrait +{ + /** + * Get language from country. + * + * @param string $country Country + * + * @return array + * + * @since 1.0.0 + */ + public static function languageFromCountry(string $country) : array + { + switch (\strtoupper($country)) { + case ISO3166TwoEnum::_AFG: + return [self::_PS, self::_UZ, self::_TK]; + case ISO3166TwoEnum::_ALA: + return [self::_SV]; + case ISO3166TwoEnum::_ALB: + return [self::_SQ]; + case ISO3166TwoEnum::_DZA: + return [self::_AR]; + case ISO3166TwoEnum::_ASM: + return [self::_EN, self::_SM]; + case ISO3166TwoEnum::_AND: + return [self::_CA]; + case ISO3166TwoEnum::_AGO: + return [self::_PT]; + case ISO3166TwoEnum::_AIA: + return [self::_EN]; + case ISO3166TwoEnum::_ATG: + return [self::_EN]; + case ISO3166TwoEnum::_ARG: + return [self::_ES, self::_GN]; + case ISO3166TwoEnum::_ARM: + return [self::_HY, self::_RU]; + case ISO3166TwoEnum::_ABW: + return [self::_NL]; + case ISO3166TwoEnum::_AUS: + return [self::_EN]; + case ISO3166TwoEnum::_AUT: + return [self::_DE]; + case ISO3166TwoEnum::_AZE: + return [self::_AZ, self::_RU, self::_HY]; + case ISO3166TwoEnum::_BHS: + return [self::_EN]; + case ISO3166TwoEnum::_BHR: + return [self::_AR]; + case ISO3166TwoEnum::_BGD: + return [self::_BN]; + case ISO3166TwoEnum::_BRB: + return [self::_EN]; + case ISO3166TwoEnum::_BLR: + return [self::_BE, self::_RU]; + case ISO3166TwoEnum::_BEL: + return [self::_NL, self::_FR, self::_DE]; + case ISO3166TwoEnum::_BLZ: + return [self::_EN]; + case ISO3166TwoEnum::_BEN: + return [self::_FR]; + case ISO3166TwoEnum::_BMU: + return [self::_EN]; + case ISO3166TwoEnum::_BTN: + return [self::_DZ]; + case ISO3166TwoEnum::_BOL: + return [self::_ES, self::_QU, self::_AY]; + case ISO3166TwoEnum::_BES: + return [self::_NL, self::_EN]; + case ISO3166TwoEnum::_BIH: + return [self::_BS, self::_HR, self::_SR]; + case ISO3166TwoEnum::_BWA: + return [self::_EN, self::_TN]; + case ISO3166TwoEnum::_BVT: + return []; + case ISO3166TwoEnum::_BRA: + return [self::_PT]; + case ISO3166TwoEnum::_IOT: + return [self::_EN]; + case ISO3166TwoEnum::_BRN: + return [self::_MS]; + case ISO3166TwoEnum::_BGR: + return [self::_BG]; + case ISO3166TwoEnum::_BFA: + return [self::_FR]; + case ISO3166TwoEnum::_BDI: + return [self::_RN, self::_FR]; + case ISO3166TwoEnum::_CPV: + return [self::_PT]; + case ISO3166TwoEnum::_KHM: + return [self::_KM]; + case ISO3166TwoEnum::_CMR: + return [self::_FR, self::_EN]; + case ISO3166TwoEnum::_CAN: + return [self::_EN, self::_FR]; + case ISO3166TwoEnum::_CYM: + return [self::_EN]; + case ISO3166TwoEnum::_CAF: + return [self::_FR, self::_SG]; + case ISO3166TwoEnum::_TCD: + return [self::_FR, self::_AR]; + case ISO3166TwoEnum::_CHL: + return [self::_ES]; + break; + case ISO3166TwoEnum::_CHN: + return [self::_ZH]; + break; + case ISO3166TwoEnum::_CXR: + return [self::_EN]; + break; + case ISO3166TwoEnum::_CCK: + return [self::_EN]; + case ISO3166TwoEnum::_COL: + return [self::_ES]; + case ISO3166TwoEnum::_COM: + return [self::_AR, self::_FR]; + case ISO3166TwoEnum::_COG: + return [self::_FR, self::_LN, self::_KG, self::_SW]; + case ISO3166TwoEnum::_COD: + return [self::_FR, self::_LN, self::_KG, self::_SW]; + case ISO3166TwoEnum::_COK: + return [self::_EN]; + case ISO3166TwoEnum::_CRI: + return [self::_ES]; + case ISO3166TwoEnum::_CIV: + return [self::_FR]; + case ISO3166TwoEnum::_HRV: + return [self::_HR]; + case ISO3166TwoEnum::_CUB: + return [self::_ES]; + case ISO3166TwoEnum::_CUW: + return [self::_NL, self::_PA, self::_EN]; + case ISO3166TwoEnum::_CYP: + return [self::_EL, self::_TR]; + case ISO3166TwoEnum::_CZE: + return [self::_CS, self::_SK]; + case ISO3166TwoEnum::_DNK: + return [self::_DA]; + case ISO3166TwoEnum::_DJI: + return [self::_FR, self::_AR, self::_SO]; + case ISO3166TwoEnum::_DMA: + return [self::_EN]; + case ISO3166TwoEnum::_DOM: + return [self::_ES]; + case ISO3166TwoEnum::_ECU: + return [self::_ES]; + case ISO3166TwoEnum::_EGY: + return [self::_AR]; + case ISO3166TwoEnum::_SLV: + return [self::_ES]; + case ISO3166TwoEnum::_GNQ: + return [self::_ES, self::_FR, self::_PT]; + case ISO3166TwoEnum::_ERI: + return [self::_TI, self::_AR, self::_EN]; + case ISO3166TwoEnum::_EST: + return [self::_ET]; + case ISO3166TwoEnum::_ETH: + return [self::_AM, self::_OM, self::_TI, self::_SO, self::_AR]; + case ISO3166TwoEnum::_FLK: + return [self::_EN]; + case ISO3166TwoEnum::_FRO: + return [self::_FO]; + case ISO3166TwoEnum::_FJI: + return [self::_EN, self::_FJ, self::_HI, self::_UR]; + case ISO3166TwoEnum::_FIN: + return [self::_FI, self::_SV]; + case ISO3166TwoEnum::_FRA: + return [self::_FR]; + case ISO3166TwoEnum::_GUF: + return [self::_FR]; + case ISO3166TwoEnum::_PYF: + return [self::_FR, self::_TY]; + case ISO3166TwoEnum::_ATF: + return [self::_FR]; + case ISO3166TwoEnum::_GAB: + return [self::_FR]; + case ISO3166TwoEnum::_GMB: + return [self::_EN]; + case ISO3166TwoEnum::_GEO: + return [self::_KA]; + case ISO3166TwoEnum::_DEU: + return [self::_DE]; + case ISO3166TwoEnum::_GHA: + return [self::_EN]; + case ISO3166TwoEnum::_GIB: + return [self::_EN]; + case ISO3166TwoEnum::_GRC: + return [self::_EL]; + case ISO3166TwoEnum::_GRL: + return [self::_KL]; + case ISO3166TwoEnum::_GRD: + return [self::_EN]; + case ISO3166TwoEnum::_GLP: + return [self::_FR]; + case ISO3166TwoEnum::_GUM: + return [self::_EN, self::_CH, self::_ES]; + case ISO3166TwoEnum::_GTM: + return [self::_ES]; + case ISO3166TwoEnum::_GGY: + return [self::_EN, self::_FR]; + case ISO3166TwoEnum::_GIN: + return [self::_FR]; + case ISO3166TwoEnum::_GNB: + return [self::_PT]; + case ISO3166TwoEnum::_GUY: + return [self::_EN]; + case ISO3166TwoEnum::_HTI: + return [self::_FR, self::_HT]; + case ISO3166TwoEnum::_HMD: + return [self::_EN]; + case ISO3166TwoEnum::_VAT: + return [self::_IT, self::_LA]; + case ISO3166TwoEnum::_HND: + return [self::_ES]; + case ISO3166TwoEnum::_HKG: + return [self::_ZH, self::_EN]; + case ISO3166TwoEnum::_HUN: + return [self::_HU]; + case ISO3166TwoEnum::_ISL: + return [self::_IS]; + case ISO3166TwoEnum::_IND: + return [self::_HI, self::_EN]; + case ISO3166TwoEnum::_IDN: + return [self::_ID]; + case ISO3166TwoEnum::_IRN: + return [self::_FA]; + case ISO3166TwoEnum::_IRQ: + return [self::_AR, self::_KU]; + case ISO3166TwoEnum::_IRL: + return [self::_GA, self::_EN]; + case ISO3166TwoEnum::_IMN: + return [self::_EN, self::_GV]; + case ISO3166TwoEnum::_ISR: + return [self::_HE, self::_AR]; + case ISO3166TwoEnum::_ITA: + return [self::_IT]; + case ISO3166TwoEnum::_JAM: + return [self::_EN]; + case ISO3166TwoEnum::_JPN: + return [self::_JA]; + case ISO3166TwoEnum::_JEY: + return [self::_EN, self::_FR]; + case ISO3166TwoEnum::_JOR: + return [self::_AR]; + case ISO3166TwoEnum::_KAZ: + return [self::_KK, self::_RU]; + case ISO3166TwoEnum::_KEN: + return [self::_SW, self::_EN]; + case ISO3166TwoEnum::_KIR: + return [self::_EN]; + case ISO3166TwoEnum::_PRK: + return [self::_KO]; + case ISO3166TwoEnum::_KOR: + return [self::_KO]; + case ISO3166TwoEnum::_KWT: + return [self::_AR]; + case ISO3166TwoEnum::_KGZ: + return [self::_KY, self::_RU]; + case ISO3166TwoEnum::_LAO: + return [self::_LO]; + case ISO3166TwoEnum::_LVA: + return [self::_LV]; + case ISO3166TwoEnum::_LBN: + return [self::_AR, self::_FR]; + case ISO3166TwoEnum::_LSO: + return [self::_EN, self::_ST]; + case ISO3166TwoEnum::_LBR: + return [self::_EN]; + case ISO3166TwoEnum::_LBY: + return [self::_AR]; + case ISO3166TwoEnum::_LIE: + return [self::_DE]; + case ISO3166TwoEnum::_LTU: + return [self::_LT]; + case ISO3166TwoEnum::_LUX: + return [self::_LB, self::_FR, self::_DE]; + case ISO3166TwoEnum::_MAC: + return [self::_ZH, self::_PT]; + case ISO3166TwoEnum::_MDG: + return [self::_MG, self::_FR]; + case ISO3166TwoEnum::_MWI: + return [self::_NY, self::_EN]; + case ISO3166TwoEnum::_MYS: + return [self::_MS]; + case ISO3166TwoEnum::_MDV: + return [self::_DV]; + case ISO3166TwoEnum::_MLI: + return [self::_FR]; + case ISO3166TwoEnum::_MLT: + return [self::_MT, self::_EN]; + case ISO3166TwoEnum::_MHL: + return [self::_MH, self::_EN]; + case ISO3166TwoEnum::_MTQ: + return [self::_FR]; + case ISO3166TwoEnum::_MRT: + return [self::_AR, self::_FR]; + case ISO3166TwoEnum::_MUS: + return [self::_EN, self::_FR]; + case ISO3166TwoEnum::_MYT: + return [self::_FR]; + case ISO3166TwoEnum::_MEX: + return [self::_ES]; + case ISO3166TwoEnum::_FSM: + return [self::_EN]; + case ISO3166TwoEnum::_MDA: + return [self::_RO]; + case ISO3166TwoEnum::_MCO: + return [self::_FR]; + case ISO3166TwoEnum::_MNG: + return [self::_MN]; + case ISO3166TwoEnum::_MNE: + return [self::_SR, self::_BS, self::_SQ, self::_HR]; + case ISO3166TwoEnum::_MSR: + return [self::_EN]; + case ISO3166TwoEnum::_MAR: + return [self::_AR]; + case ISO3166TwoEnum::_MOZ: + return [self::_PT]; + case ISO3166TwoEnum::_MMR: + return [self::_MY]; + case ISO3166TwoEnum::_NAM: + return [self::_EN, self::_AF]; + case ISO3166TwoEnum::_NRU: + return [self::_NA, self::_EN]; + case ISO3166TwoEnum::_NPL: + return [self::_NE]; + case ISO3166TwoEnum::_NLD: + return [self::_NL]; + case ISO3166TwoEnum::_NCL: + return [self::_FR]; + case ISO3166TwoEnum::_NZL: + return [self::_EN, self::_MI]; + case ISO3166TwoEnum::_NIC: + return [self::_ES]; + case ISO3166TwoEnum::_NER: + return [self::_FR]; + case ISO3166TwoEnum::_NGA: + return [self::_EN]; + case ISO3166TwoEnum::_NIU: + return [self::_EN]; + case ISO3166TwoEnum::_NFK: + return [self::_EN]; + case ISO3166TwoEnum::_MNP: + return [self::_EN, self::_CH]; + case ISO3166TwoEnum::_NOR: + return [self::_NO, self::_NB, self::_NN]; + case ISO3166TwoEnum::_OMN: + return [self::_AR]; + case ISO3166TwoEnum::_PAK: + return [self::_UR, self::_EN]; + case ISO3166TwoEnum::_PLW: + return [self::_EN, self::_JA, self::_ZH]; + case ISO3166TwoEnum::_PSE: + return [self::_AR]; + case ISO3166TwoEnum::_PAN: + return [self::_ES]; + case ISO3166TwoEnum::_PNG: + return [self::_EN, self::_HO]; + case ISO3166TwoEnum::_PRY: + return [self::_ES, self::_GN]; + case ISO3166TwoEnum::_PER: + return [self::_ES, self::_QU, self::_AY]; + case ISO3166TwoEnum::_PHL: + return [self::_EN]; + case ISO3166TwoEnum::_PCN: + return [self::_EN]; + case ISO3166TwoEnum::_POL: + return [self::_PL]; + case ISO3166TwoEnum::_PRT: + return [self::_PT]; + case ISO3166TwoEnum::_PRI: + return [self::_ES, self::_EN]; + case ISO3166TwoEnum::_QAT: + return [self::_AR]; + case ISO3166TwoEnum::_REU: + return [self::_FR]; + case ISO3166TwoEnum::_ROU: + return [self::_RO]; + case ISO3166TwoEnum::_RUS: + return [self::_RU]; + case ISO3166TwoEnum::_RWA: + return [self::_RW, self::_EN, self::_FR]; + case ISO3166TwoEnum::_BLM: + return [self::_FR]; + case ISO3166TwoEnum::_SHN: + return [self::_EN]; + case ISO3166TwoEnum::_KNA: + return [self::_EN]; + case ISO3166TwoEnum::_LCA: + return [self::_EN]; + case ISO3166TwoEnum::_MAF: + return [self::_FR, self::_EN, self::_NL]; + case ISO3166TwoEnum::_SPM: + return [self::_FR]; + case ISO3166TwoEnum::_VCT: + return [self::_EN]; + case ISO3166TwoEnum::_WSM: + return [self::_SM, self::_EN]; + case ISO3166TwoEnum::_SMR: + return [self::_IT]; + case ISO3166TwoEnum::_STP: + return [self::_PT]; + case ISO3166TwoEnum::_SAU: + return [self::_AR]; + case ISO3166TwoEnum::_SEN: + return [self::_FR, self::_WO]; + case ISO3166TwoEnum::_SRB: + return [self::_SR]; + case ISO3166TwoEnum::_SYC: + return [self::_FR, self::_EN]; + case ISO3166TwoEnum::_SLE: + return [self::_EN]; + case ISO3166TwoEnum::_SGP: + return [self::_EN, self::_MS, self::_TA, self::_ZH]; + case ISO3166TwoEnum::_SXM: + return [self::_NL, self::_EN]; + case ISO3166TwoEnum::_SVK: + return [self::_SK]; + case ISO3166TwoEnum::_SVN: + return [self::_SL]; + case ISO3166TwoEnum::_SLB: + return [self::_EN]; + case ISO3166TwoEnum::_SOM: + return [self::_SO, self::_AR, self::_IT, self::_EN]; + case ISO3166TwoEnum::_ZAF: + return [self::_ZU, self::_XH, self::_AF, self::_EN, self::_TN, self::_ST, self::_TS, self::_SS, self::_VE]; + case ISO3166TwoEnum::_SGS: + return [self::_EN]; + case ISO3166TwoEnum::_KOR: + return [self::_KO]; + case ISO3166TwoEnum::_SSD: + return [self::_EN]; + case ISO3166TwoEnum::_ESP: + return [self::_ES]; + case ISO3166TwoEnum::_LKA: + return [self::_SI, self::_TA, self::_EN]; + case ISO3166TwoEnum::_SDN: + return [self::_AR, self::_EN]; + case ISO3166TwoEnum::_SUR: + return [self::_NL]; + case ISO3166TwoEnum::_SJM: + return [self::_NO]; + case ISO3166TwoEnum::_SWZ: + return [self::_EN, self::_SS]; + case ISO3166TwoEnum::_SWE: + return [self::_SV]; + case ISO3166TwoEnum::_CHE: + return [self::_DE, self::_FR, self::_IT]; + case ISO3166TwoEnum::_SYR: + return [self::_AR]; + case ISO3166TwoEnum::_TWN: + return [self::_ZH]; + case ISO3166TwoEnum::_TJK: + return [self::_TG, self::_RU]; + case ISO3166TwoEnum::_TZA: + return [self::_SW, self::_EN]; + case ISO3166TwoEnum::_THA: + return [self::_TH]; + case ISO3166TwoEnum::_TLS: + return [self::_PT]; + case ISO3166TwoEnum::_TGO: + return [self::_FR]; + case ISO3166TwoEnum::_TKL: + return [self::_EN]; + case ISO3166TwoEnum::_TON: + return [self::_EN, self::_TO]; + case ISO3166TwoEnum::_TTO: + return [self::_EN]; + case ISO3166TwoEnum::_TUN: + return [self::_AR]; + case ISO3166TwoEnum::_TUR: + return [self::_TR]; + case ISO3166TwoEnum::_TKM: + return [self::_TK, self::_RU]; + case ISO3166TwoEnum::_TCA: + return [self::_EN]; + case ISO3166TwoEnum::_TUV: + return [self::_EN]; + case ISO3166TwoEnum::_UGA: + return [self::_EN, self::_SW]; + case ISO3166TwoEnum::_UKR: + return [self::_UK]; + case ISO3166TwoEnum::_ARE: + return [self::_AR]; + case ISO3166TwoEnum::_GBR: + return [self::_EN, self::_CY, self::_GD, self::_GA]; + case ISO3166TwoEnum::_USA: + return [self::_EN, self::_ES]; + case ISO3166TwoEnum::_UMI: + return [self::_EN]; + case ISO3166TwoEnum::_URY: + return [self::_ES]; + case ISO3166TwoEnum::_UZB: + return [self::_UZ, self::_RU]; + case ISO3166TwoEnum::_VUT: + return [self::_BI, self::_EN, self::_FR]; + case ISO3166TwoEnum::_VEN: + return [self::_ES]; + case ISO3166TwoEnum::_VNM: + return [self::_VI]; + case ISO3166TwoEnum::_VGB: + return [self::_EN]; + case ISO3166TwoEnum::_VIR: + return [self::_EN]; + case ISO3166TwoEnum::_WLF: + return [self::_FR]; + case ISO3166TwoEnum::_ESH: + return [self::_AR, self::_ES]; + case ISO3166TwoEnum::_YEM: + return [self::_AR]; + case ISO3166TwoEnum::_ZMB: + return [self::_EN]; + case ISO3166TwoEnum::_ZWE: + return [self::_EN, self::_SN, self::_ND]; + default: + return []; + } + } +} diff --git a/Localization/ISO639Enum.php b/Localization/ISO639Enum.php index d50518cdd..6f3f83332 100755 --- a/Localization/ISO639Enum.php +++ b/Localization/ISO639Enum.php @@ -118,6 +118,8 @@ class ISO639Enum extends Enum public const _FJ = 'Fijian'; + public const _FIL = 'Filipino'; + public const _FO = 'Faroese'; public const _FR = 'French'; @@ -393,4 +395,6 @@ class ISO639Enum extends Enum public const _ZH = 'Chinese'; public const _ZU = 'Zulu'; + + use ISO639CountryTrait; } diff --git a/Localization/ISO639x1Enum.php b/Localization/ISO639x1Enum.php index 3cf845109..b787646e8 100755 --- a/Localization/ISO639x1Enum.php +++ b/Localization/ISO639x1Enum.php @@ -74,6 +74,8 @@ class ISO639x1Enum extends Enum public const _BG = 'bg'; + public const _MS = 'ms'; + public const _MY = 'my'; public const _CA = 'ca'; @@ -86,6 +88,8 @@ class ISO639x1Enum extends Enum public const _CV = 'cv'; + public const _KU = 'ku'; + public const _KW = 'kw'; public const _KO = 'ko'; @@ -120,6 +124,8 @@ class ISO639x1Enum extends Enum public const _FI = 'fi'; + public const _FIL = 'fil'; + public const _FR = 'fr'; public const _FF = 'ff'; @@ -262,6 +268,8 @@ class ISO639x1Enum extends Enum public const _OS = 'os'; + public const _PA = 'pa'; + public const _PI = 'pi'; public const _FA = 'fa'; @@ -322,6 +330,8 @@ class ISO639x1Enum extends Enum public const _TE = 'te'; + public const _TG = 'tg'; + public const _TH = 'th'; public const _TI = 'ti'; @@ -348,6 +358,8 @@ class ISO639x1Enum extends Enum public const _UR = 'ur'; + public const _UZ = 'uz'; + public const _VE = 've'; public const _VI = 'vi'; @@ -373,4 +385,6 @@ class ISO639x1Enum extends Enum public const _ZU = 'zu'; public const _ZH = 'zh'; + + use ISO639CountryTrait; } diff --git a/Localization/ISO639x2Enum.php b/Localization/ISO639x2Enum.php index e401c0965..4941a4557 100755 --- a/Localization/ISO639x2Enum.php +++ b/Localization/ISO639x2Enum.php @@ -120,6 +120,8 @@ class ISO639x2Enum extends Enum public const _FI = 'fin'; + public const _FIL = 'fil'; + public const _FR = 'fra'; public const _FF = 'ful'; @@ -393,4 +395,6 @@ class ISO639x2Enum extends Enum public const _ZA = 'zha'; public const _ZU = 'zul'; + + use ISO639CountryTrait; } diff --git a/Localization/L11nManager.php b/Localization/L11nManager.php index 19996a480..f48553eb6 100755 --- a/Localization/L11nManager.php +++ b/Localization/L11nManager.php @@ -17,6 +17,7 @@ namespace phpOMS\Localization; use phpOMS\Autoloader; use phpOMS\Log\FileLogger; use phpOMS\Module\ModuleAbstract; +use phpOMS\Stdlib\Base\FloatInt; /** * Localization class. @@ -206,22 +207,31 @@ final class L11nManager /** * Print a numeric value * - * @param Localization $l11n Localization - * @param int|float $numeric Numeric value to print - * @param null|string $format Format type to use + * @param Localization $l11n Localization + * @param int|float|FloatInt $numeric Numeric value to print + * @param null|string $format Format type to use * * @return string * * @since 1.0.0 */ - public function getNumeric(Localization $l11n, int | float $numeric, string $format = null) : string + public function getNumeric(Localization $l11n, int | float | FloatInt $numeric, string $format = null) : string { - return \number_format( - $numeric, - $l11n->getPrecision()[$format ?? 'medium'], - $l11n->getDecimal(), - $l11n->getThousands() + if (!($numeric instanceof FloatInt)) { + return \number_format( + $numeric, + $l11n->getPrecision()[$format ?? 'medium'], + $l11n->getDecimal(), + $l11n->getThousands() + ); + } + + $numeric->setLocalization( + $l11n->getThousands(), + $l11n->getDecimal() ); + + return $numeric->getAmount($l11n->getPrecision()[$format ?? 'medium']); } /** @@ -247,17 +257,23 @@ final class L11nManager /** * Print a currency * - * @param Localization $l11n Localization - * @param int|float $currency Currency value to print - * @param null|string $format Format type to use - * @param null|string $symbol Currency name/symbol - * @param int $divide Divide currency by divisor + * @param Localization $l11n Localization + * @param int|float|Money $currency Currency value to print + * @param null|string $symbol Currency name/symbol + * @param null|string $format Format type to use + * @param int $divide Divide currency by divisor * * @return string * * @since 1.0.0 */ - public function getCurrency(Localization $l11n, int | float $currency, string $format = null, string $symbol = null, int $divide = 1) : string + public function getCurrency( + Localization $l11n, + int | float | Money $currency, + string $symbol = null, + string $format = null, + int $divide = 1 + ) : string { $language = $l11n->getLanguage(); $symbol ??= $l11n->getCurrency(); @@ -266,7 +282,7 @@ final class L11nManager $currency = (int) ($currency * \pow(10, Money::MAX_DECIMALS)); } - if (!empty($symbol)) { + if ($divide > 1 && !empty($symbol)) { if ($divide === 1000) { $symbol = $this->getHtml($language, '0', '0', 'CurrencyK') . $symbol; } elseif ($divide === 1000000) { @@ -276,7 +292,10 @@ final class L11nManager } } - $money = new Money((int) ($currency / $divide)); + $money = !($currency instanceof Money) + ? new Money((int) ($currency / $divide)) + : $currency; + $money->setLocalization( $l11n->getThousands(), $l11n->getDecimal(), diff --git a/Localization/LanguageDetection/NgramParser.php b/Localization/LanguageDetection/NgramParser.php index 878923eb8..e23fef99e 100644 --- a/Localization/LanguageDetection/NgramParser.php +++ b/Localization/LanguageDetection/NgramParser.php @@ -108,14 +108,14 @@ abstract class NgramParser } } - if (!\count($tokens)) { + if (empty($tokens)) { return []; } $tokens = \array_merge(...$tokens); unset($tokens['_']); - \arsort($tokens, SORT_NUMERIC); + \arsort($tokens, \SORT_NUMERIC); return \array_slice( \array_keys($tokens), diff --git a/Localization/LanguageDetection/Tokenizer/WhitespaceTokenizer.php b/Localization/LanguageDetection/Tokenizer/WhitespaceTokenizer.php index 722a10ca0..3f221dbc3 100644 --- a/Localization/LanguageDetection/Tokenizer/WhitespaceTokenizer.php +++ b/Localization/LanguageDetection/Tokenizer/WhitespaceTokenizer.php @@ -39,7 +39,7 @@ class WhitespaceTokenizer function ($word) { return "_{$word}_"; }, - \preg_split('/[^\pL]+(?country = ''; } $this->country = $country; diff --git a/Math/Stochastic/NaiveBayesClassifier.php b/Math/Stochastic/NaiveBayesClassifier.php index d0d195890..8d28325a1 100755 --- a/Math/Stochastic/NaiveBayesClassifier.php +++ b/Math/Stochastic/NaiveBayesClassifier.php @@ -201,7 +201,7 @@ final class NaiveBayesClassifier $this->probabilities['attr'][$attr] = ['data' => []]; } - foreach ($valueArray['data'] as $word => $count) { + foreach ($valueArray['data'] as $word => $_) { if (!isset($this->dict[$criteria][$attr]['data'][$word])) { continue; } diff --git a/Message/Http/HttpRequest.php b/Message/Http/HttpRequest.php index d2294b13f..273c281e1 100755 --- a/Message/Http/HttpRequest.php +++ b/Message/Http/HttpRequest.php @@ -17,6 +17,7 @@ namespace phpOMS\Message\Http; use phpOMS\Localization\Localization; use phpOMS\Message\RequestAbstract; use phpOMS\Router\RouteVerb; +use phpOMS\Security\Guard; use phpOMS\Uri\HttpUri; use phpOMS\Uri\UriInterface; @@ -94,6 +95,7 @@ final class HttpRequest extends RequestAbstract self::cleanupGlobals(); $this->data = \array_change_key_case($this->data, \CASE_LOWER); + $this->data = Guard::unslash($this->data); } /** @@ -106,9 +108,10 @@ final class HttpRequest extends RequestAbstract private function initCurrentRequest() : void { $this->uri = HttpUri::fromCurrent(); - $this->data = $_GET + $_POST; + $this->data = $_POST + $_GET; $this->files = $_FILES; $this->header->l11n->setLanguage($this->getRequestLanguage()); + $this->header->l11n->setCountry($this->getRequestCountry()); $this->initNonGetData(); } @@ -381,6 +384,31 @@ final class HttpRequest extends RequestAbstract return \strtolower($firstLocalComponents[0]); // @codeCoverageIgnore } + /** + * Get request language + * + * @return string + * + * @since 1.0.0 + */ + public function getRequestCountry() : string + { + if (!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { + return ''; + } + + // @codeCoverageIgnoreStart + $components = \explode(';', $_SERVER['HTTP_ACCEPT_LANGUAGE']); + $locals = \stripos($components[0], ',') !== false + ? $locals = \explode(',', $components[0]) + : $components; + + $firstLocalComponents = \explode('-', $locals[0]); + // @codeCoverageIgnoreEnd + + return \strtoupper($firstLocalComponents[1] ?? ''); // @codeCoverageIgnore + } + /** * Get request locale * @@ -396,7 +424,9 @@ final class HttpRequest extends RequestAbstract // @codeCoverageIgnoreStart $components = \explode(';', $_SERVER['HTTP_ACCEPT_LANGUAGE']); - $locals = \stripos($components[0], ',') !== false ? $locals = \explode(',', $components[0]) : $components; + $locals = \stripos($components[0], ',') !== false + ? $locals = \explode(',', $components[0]) + : $components; // @codeCoverageIgnoreEnd return \str_replace('-', '_', $locals[0]); // @codeCoverageIgnore diff --git a/Message/Http/Rest.php b/Message/Http/Rest.php index 453f33924..08d1449f4 100755 --- a/Message/Http/Rest.php +++ b/Message/Http/Rest.php @@ -92,7 +92,7 @@ final class Rest /* @phpstan-ignore-next-line */ $data = self::createMultipartData($boundary, $request->getData()); - // @todo: replace boundary/ with the correct boundary= in the future. + // @todo: Replace boundary/ with the correct boundary= in the future. // Currently this cannot be done due to a bug. If we do it now the server cannot correclty populate php://input $headers['content-type'] = 'Content-Type: multipart/form-data; boundary/' . $boundary; $headers['content-length'] = 'Content-Length: ' . \strlen($data); diff --git a/Message/RequestAbstract.php b/Message/RequestAbstract.php index 4cabaeef4..2c131152b 100755 --- a/Message/RequestAbstract.php +++ b/Message/RequestAbstract.php @@ -336,6 +336,18 @@ abstract class RequestAbstract implements MessageInterface return $this->header->l11n->getLanguage(); } + /** + * Get request language. + * + * @return string + * + * @since 1.0.0 + */ + public function getCountry() : string + { + return $this->header->l11n->getCountry(); + } + /** * Get request hash. * diff --git a/Model/Html/Head.php b/Model/Html/Head.php index 885e6db3c..92cf0b8a2 100755 --- a/Model/Html/Head.php +++ b/Model/Html/Head.php @@ -101,8 +101,9 @@ final class Head implements RenderableInterface /** * Add asset. * - * @param int $type Asset type - * @param string $uri Asset uri + * @param int $type Asset type + * @param string $uri Asset uri + * @param array $attributes Asset attributes * * @return void * @@ -116,8 +117,9 @@ final class Head implements RenderableInterface /** * Add tag. * - * @param int $type Asset type - * @param string $uri Asset uri + * @param int $type Asset type + * @param string $content Asset content + * @param array $attributes Asset attributes * * @return void * diff --git a/Router/RouterInterface.php b/Router/RouterInterface.php index cc4b1f7b1..23b29210e 100755 --- a/Router/RouterInterface.php +++ b/Router/RouterInterface.php @@ -74,7 +74,7 @@ interface RouterInterface * @param string $uri Route * @param string $csrf CSRF token * @param int $verb Route verb - * @param string $app Application name + * @param int $app Application name * @param int $unitId Organization id * @param Account $account Account * @param array $data Data @@ -87,7 +87,7 @@ interface RouterInterface string $uri, string $csrf = null, int $verb = RouteVerb::GET, - string $app = null, + int $app = null, int $unitId = null, Account $account = null, array $data = null diff --git a/Router/SocketRouter.php b/Router/SocketRouter.php index db7ca35ac..3d14fae5f 100755 --- a/Router/SocketRouter.php +++ b/Router/SocketRouter.php @@ -113,7 +113,7 @@ final class SocketRouter implements RouterInterface string $uri, string $csrf = null, int $verb = RouteVerb::GET, - string $app = null, + int $app = null, int $unitId = null, Account $account = null, array $data = null diff --git a/Router/WebRouter.php b/Router/WebRouter.php index b5c07cd78..f26b2f281 100755 --- a/Router/WebRouter.php +++ b/Router/WebRouter.php @@ -115,7 +115,7 @@ final class WebRouter implements RouterInterface string $uri, string $csrf = null, int $verb = RouteVerb::GET, - string $app = null, + int $app = null, int $unitId = null, Account $account = null, array $data = null diff --git a/Security/Guard.php b/Security/Guard.php index 92a980ada..eb7c6e197 100644 --- a/Security/Guard.php +++ b/Security/Guard.php @@ -52,4 +52,29 @@ final class Guard { return \stripos(FileUtils::absolute($path), FileUtils::absolute(empty($base) ? self::$BASE_PATH : $base)) === 0; } + + /** + * Remove slashes from a string or array + * + * @param string|array $data Data to unslash + * + * @return string|array + * + * @since 1.0.0 + */ + public static function unslash(string | array $data) : string|array + { + if (\is_array($data)) { + $result = []; + foreach ($data as $key => $value) { + $result[$key] = self::unslash($value); + } + + return $result; + } elseif (\is_string($data)) { + return \stripslashes($data); + } + + return $data; + } } diff --git a/Stdlib/Graph/Graph.php b/Stdlib/Graph/Graph.php index 75402db2f..3b8104161 100755 --- a/Stdlib/Graph/Graph.php +++ b/Stdlib/Graph/Graph.php @@ -633,6 +633,18 @@ class Graph return $longestPath; } + /** + * Perform depth first traversal + * + * @param Node $node Graph node + * @param array $visited Is the node already visited + * @param array $path Array of nodes (a path through the graph = connected nodes) + * @param array $longestPath Array of nodes (longest path through the graph = connected nodes) + * + * @return void + * + * @since 1.0.0 + */ private function longestPathDfs(Node $node, &$visited, &$path, &$longestPath) { $visited[$node->getId()] = true; diff --git a/Stdlib/Graph/Node.php b/Stdlib/Graph/Node.php index fd6829379..9a888b0d1 100755 --- a/Stdlib/Graph/Node.php +++ b/Stdlib/Graph/Node.php @@ -172,6 +172,15 @@ class Node return $this->edges[$key] ?? null; } + /** + * Check if the node has a certain neighbor + * + * @param Node $node Neighbor node + * + * @return bool + * + * @since 1.0.0 + */ public function hasNeighbor(self $node) : bool { foreach ($this->edges as $edge) { diff --git a/Stdlib/Map/MultiMap.php b/Stdlib/Map/MultiMap.php index 9d384756f..deea5ccef 100755 --- a/Stdlib/Map/MultiMap.php +++ b/Stdlib/Map/MultiMap.php @@ -166,7 +166,7 @@ final class MultiMap implements \Countable */ private function garbageCollectValues() : void { - foreach ($this->values as $valueKey => $value) { + foreach ($this->values as $valueKey => $_) { if (!\in_array($valueKey, $this->keys)) { unset($this->values[$valueKey]); } diff --git a/System/File/Ftp/Directory.php b/System/File/Ftp/Directory.php index bcd019d8f..20ac461f7 100755 --- a/System/File/Ftp/Directory.php +++ b/System/File/Ftp/Directory.php @@ -391,6 +391,8 @@ class Directory extends FileAbstract implements DirectoryInterface continue; } + $e = []; + list( $e['permission'], $e['number'], diff --git a/Uri/UriFactory.php b/Uri/UriFactory.php index f297407d1..3b3bbc78e 100755 --- a/Uri/UriFactory.php +++ b/Uri/UriFactory.php @@ -86,7 +86,7 @@ final class UriFactory if ($identifier === '*') { self::$uri = []; } else { - foreach (self::$uri as $key => $value) { + foreach (self::$uri as $key => $_) { if (\stripos($key, $identifier) === 0) { unset(self::$uri[$key]); } diff --git a/Utils/Barcode/QR.php b/Utils/Barcode/QR.php index 601793d43..82bde7fc5 100755 --- a/Utils/Barcode/QR.php +++ b/Utils/Barcode/QR.php @@ -794,7 +794,7 @@ class QR extends TwoDAbstract { $b = 0; $bitMask = []; - $bitMask = $this->generateMaskNo($maskNo, $width, $s, $d); + $bitMask = $this->generateMaskNo($maskNo, $width, $s); if ($maskGenOnly) { return 0; diff --git a/Utils/Converter/Currency.php b/Utils/Converter/Currency.php index d981eface..2567238ea 100755 --- a/Utils/Converter/Currency.php +++ b/Utils/Converter/Currency.php @@ -114,7 +114,7 @@ final class Currency $node = $xml->Cube->Cube->Cube; self::$ecbCurrencies = []; - foreach ($node as $key => $value) { + foreach ($node as $value) { /** @var null|array $attributes */ if (($attributes = $value->attributes()) === null) { continue; diff --git a/Utils/Parser/Calendar/ICalParser.php b/Utils/Parser/Calendar/ICalParser.php index b3e81a519..2f4f822b0 100644 --- a/Utils/Parser/Calendar/ICalParser.php +++ b/Utils/Parser/Calendar/ICalParser.php @@ -12,9 +12,27 @@ */ declare(strict_types=1); +/** + * iCal parser. + * + * @package phpOMS\Utils\Parser\Calendar + * @license OMS License 2.0 + * @link https://jingga.app + * @since 1.0.0 + */ class ICalParser { - public static function parse(string $data) { + /** + * Parse iCal data + * + * @param string $data iCal data + * + * @return array + * + * @since 1.0.0 + */ + public static function parse(string $data) : array + { \preg_match_all('/BEGIN:VEVENT(.*?)END:VEVENT/s', $data, $matches, PREG_SET_ORDER); $eventList = []; @@ -54,8 +72,19 @@ class ICalParser $eventList[] = $event; } + + return $eventList; } + /** + * Parse rrule + * + * @param string $rruleString rrule string + * + * @return array + * + * @since 1.0.0 + */ private static function parseRRule($rruleString) : array { $rrule = []; diff --git a/Utils/Parser/Spreadsheet/SpreadsheetWriter.php b/Utils/Parser/Spreadsheet/SpreadsheetWriter.php index 9fc793023..61551d808 100644 --- a/Utils/Parser/Spreadsheet/SpreadsheetWriter.php +++ b/Utils/Parser/Spreadsheet/SpreadsheetWriter.php @@ -28,16 +28,6 @@ use PhpOffice\PhpSpreadsheet\Writer\Pdf; */ class SpreadsheetWriter extends Pdf { - /** - * Render Pdf - * - * @todo: can be removed? - * - * @var bool - * @since 1.0.0 - */ - protected bool $isMPdf = true; - /** * Save Spreadsheet to file. * @@ -47,6 +37,8 @@ class SpreadsheetWriter extends Pdf */ public function toPdfString(): string { + $this->isMPdf = true; + $pdf = new \Mpdf\Mpdf(); // Check for paper size and page orientation diff --git a/Utils/StringUtils.php b/Utils/StringUtils.php index b375a669e..881c1513c 100755 --- a/Utils/StringUtils.php +++ b/Utils/StringUtils.php @@ -290,7 +290,7 @@ final class StringUtils break; } - // @todo: this should not be necessary + // @todo: This should not be necessary but the algorithm above allows for weird combinations. return \str_replace( ['', '', '', '', '', '', ' ', ' '], ['', '', '', '', '', '', '', ''], diff --git a/Views/View.php b/Views/View.php index d9b852430..f54ef7eb2 100755 --- a/Views/View.php +++ b/Views/View.php @@ -16,10 +16,12 @@ namespace phpOMS\Views; use phpOMS\Localization\L11nManager; use phpOMS\Localization\Localization; +use phpOMS\Localization\Money; use phpOMS\Message\RequestAbstract; use phpOMS\Message\ResponseAbstract; use phpOMS\Module\Exception\InvalidModuleException; use phpOMS\Module\Exception\InvalidThemeException; +use phpOMS\Stdlib\Base\FloatInt; /** * Basic view which can be used as basis for specific implementations. @@ -308,14 +310,14 @@ class View extends ViewAbstract /** * Print a numeric value * - * @param int|float $numeric Numeric value to print - * @param null|string $format Format type to use + * @param int|float|FloatInt $numeric Numeric value to print + * @param null|string $format Format type to use * * @return string * * @since 1.0.0 */ - public function getNumeric(int | float $numeric, string $format = null) : string + public function getNumeric(int | float | FloatInt $numeric, string $format = null) : string { return $this->l11nManager->getNumeric($this->l11n, $numeric, $format); } @@ -338,18 +340,24 @@ class View extends ViewAbstract /** * Print a currency * - * @param int|float $currency Currency value to print - * @param null|string $format Format type to use - * @param null|string $symbol Currency name/symbol - * @param int $divide Divide currency by divisor + * @param int|float|Money $currency Currency value to print + * @param int $precision Precision + * @param null|string $symbol Currency name/symbol + * @param null|string $format Format type to use + * @param int $divide Divide currency by divisor * * @return string * * @since 1.0.0 */ - public function getCurrency(int | float $currency, string $format = null, string $symbol = null, int $divide = 1) : string + public function getCurrency( + int | float | Money $currency, + string $symbol = null, + string $format = null, + int $divide = 1 + ) : string { - return $this->l11nManager->getCurrency($this->l11n, $currency, $format, $symbol, $divide); + return $this->l11nManager->getCurrency($this->l11n, $currency, $symbol, $format, $divide); } /** diff --git a/tests/Api/EUVAT/EUVATBffOnlineTest.php b/tests/Api/EUVAT/EUVATBffOnlineTest.php new file mode 100644 index 000000000..598e746d6 --- /dev/null +++ b/tests/Api/EUVAT/EUVATBffOnlineTest.php @@ -0,0 +1,39 @@ +validate('DE123456789', 'DE123456789'); + + self::assertEquals(0, $status['status']); + self::assertEquals('C', $status['vat']); + } +} diff --git a/tests/Api/EUVAT/EUVATViesTest.php b/tests/Api/EUVAT/EUVATViesTest.php new file mode 100644 index 000000000..cade1022b --- /dev/null +++ b/tests/Api/EUVAT/EUVATViesTest.php @@ -0,0 +1,39 @@ +validate('DE123456789'); + + self::assertEquals(0, $status['status']); + self::assertEquals('C', $status['vat']); + } +} diff --git a/tests/Message/Http/HttpRequestLanguage.php b/tests/Message/Http/HttpRequestLanguage.php index b47bcb131..9bdb12c00 100755 --- a/tests/Message/Http/HttpRequestLanguage.php +++ b/tests/Message/Http/HttpRequestLanguage.php @@ -7,4 +7,4 @@ use phpOMS\Message\Http\HttpRequest; $request = HttpRequest::createFromSuperglobals(); -echo $request->getRequestLanguage(); +echo $request->getCountry(); diff --git a/tests/Message/Http/HttpRequestTest.php b/tests/Message/Http/HttpRequestTest.php index 9b012d3ee..b26dd3993 100755 --- a/tests/Message/Http/HttpRequestTest.php +++ b/tests/Message/Http/HttpRequestTest.php @@ -59,7 +59,7 @@ final class HttpRequestTest extends \PHPUnit\Framework\TestCase self::assertEquals('', $request->__toString()); self::assertFalse($request->hasData('key')); self::assertNull($request->getData('key')); - self::assertEquals('en', $request->getRequestLanguage()); + self::assertEquals('en', $request->getCountry()); self::assertEquals('en_US', $request->getLocale()); } diff --git a/tests/Stdlib/Graph/NodeTest.php b/tests/Stdlib/Graph/NodeTest.php index bdedd2034..040d9ffa9 100755 --- a/tests/Stdlib/Graph/NodeTest.php +++ b/tests/Stdlib/Graph/NodeTest.php @@ -135,7 +135,7 @@ final class NodeTest extends \PHPUnit\Framework\TestCase self::assertInstanceOf(Edge::class, $edge = $node1->setNodeRelative($node2, null, false)); self::assertCount(1, $node2->getEdges()); - self::assertFalse($edge->isDirected()); + self::assertFalse($edge->isDirected); } /** @@ -173,7 +173,7 @@ final class NodeTest extends \PHPUnit\Framework\TestCase $node1->setNodeRelative($node2); $node1->setNodeRelative($node3); - self::assertEquals('C', $node1->getEdgeByNeighbor($node3)->getNode2()->getId()); + self::assertEquals('C', $node1->getEdgeByNeighbor($node3)->node2->getId()); self::assertNull($node1->getEdgeByNeighbor($node4)); } }