diff --git a/.phan/config.php b/.phan/config.php index 746ceb07..32684d9d 100644 --- a/.phan/config.php +++ b/.phan/config.php @@ -26,8 +26,8 @@ use Phan\Config; return [ - // "target_php_version" => "8.2", - "minimum_target_php_version" => "8.2", + // "target_php_version" => "8.3", + "minimum_target_php_version" => "8.3", // turn color on (-C) "color_issue_messages_if_supported" => true, // If true, missing properties will be created when diff --git a/.phive/phars.xml b/.phive/phars.xml index 5a90dcfe..9c7686d2 100644 --- a/.phive/phars.xml +++ b/.phive/phars.xml @@ -1,12 +1,12 @@ - - - - - - + + + + + + - - + + diff --git a/4dev/checking/phan.sh b/4dev/checking/phan.sh index 40f9877e..b44c0c1a 100755 --- a/4dev/checking/phan.sh +++ b/4dev/checking/phan.sh @@ -1,6 +1,83 @@ -base=$(pwd)"/"; +#!/bin/env bash + +function error() { + if [ -t 1 ]; then echo "[MAK] ERROR: $*" >&2; fi; exit 0; +} + +usage() { + cat <&2; fi; exit 0; +} + +usage() { + cat < [ - 0 => '-117.42 MB', - 1 => -123123794, - 2 => -117420000, - ], - 'megabyte' => [ - 0 => '242.98 MB', - 1 => 254782996, - 2 => 242980000 - ], - 'megabyte si' => [ - 0 => '254.78 MiB', - 1 => 267156193, - 2 => 254780000 - ], - 'petabyte' => [ - 0 => '1 EiB', - 1 => 1152921504606846976, - 2 => 1000000000000000000, - ], - 'max int' => [ - 0 => '8 EB', - 1 => -9223372036854775807 - 1, - 2 => 8000000000000000000, - ], - 'exabyte, overflow' => [ - 0 => '867.36EB', - 1 => 3873816255479021568, - 2 => 363028535651074048, - ] - ]; - } - /** * Undocumented function * @@ -180,7 +139,7 @@ final class CoreLibsConvertByteTest extends TestCase * @return void */ public function testHumanReadableByteFormat( - $input, + string|int|float $input, string $expected, string $expected_si, string $expected_no_space, @@ -217,6 +176,73 @@ final class CoreLibsConvertByteTest extends TestCase ); } + /** + * Undocumented function + * + * @return array + */ + public function byteStringProvider(): array + { + return [ + 'negative number' => [ + 0 => '-117.42 MB', + 1 => -123123794, + 2 => -117420000, + 3 => "-123123793", + 4 => "-117420000", + 5 => null, + ], + 'megabyte' => [ + 0 => '242.98 MB', + 1 => 254782996, + 2 => 242980000, + 3 => "254782996", + 4 => "242980000", + 5 => null, + ], + 'megabyte si' => [ + 0 => '254.78 MiB', + 1 => 267156193, + 2 => 254780000, + 3 => "267156193", + 4 => "254780000", + 5 => null, + ], + 'petabyte' => [ + 0 => '1 EiB', + 1 => 1152921504606846976, + 2 => 1000000000000000000, + 3 => "1152921504606846976", + 4 => "1000000000000000000", + 5 => null, + ], + 'max int' => [ + 0 => '8 EB', + 1 => 0, + 2 => 0, + 3 => "9223372036854775808", + 4 => "8000000000000000000", + 5 => \LengthException::class, + ], + 'exabyte, overflow' => [ + 0 => '867.36EB', + 1 => 0, + 2 => 0, + 3 => "999997996235794808832", + 4 => "867360000000000000000", + 5 => \LengthException::class, + ], + 'huge exabyte, overflow' => [ + 0 => '1000EB', + 1 => 0, + 2 => 0, + 3 => "1152921504606846976000", + 4 => "1000000000000000000000", + 5 => \LengthException::class, + ], + ]; + } + /** * Undocumented function * @@ -227,10 +253,22 @@ final class CoreLibsConvertByteTest extends TestCase * @param string|int|float $input * @param string|int|float $expected * @param string|int|float $expected_si + * @param string|int|float $expected_string + * @param string|int|float $expected_string_si + * @param ?string $exception * @return void */ - public function testStringByteFormat($input, $expected, $expected_si): void - { + public function testStringByteFormat( + string|int|float $input, + string|int|float $expected, + string|int|float $expected_si, + string|int|float $expected_string, + string|int|float $expected_string_si, + ?string $exception + ): void { + if ($exception !== null) { + $this->expectException($exception); + } $this->assertEquals( $expected, \CoreLibs\Convert\Byte::stringByteFormat($input) @@ -239,6 +277,17 @@ final class CoreLibsConvertByteTest extends TestCase $expected_si, \CoreLibs\Convert\Byte::stringByteFormat($input, \CoreLibs\Convert\Byte::BYTE_FORMAT_SI) ); + $this->assertEquals( + $expected_string, + \CoreLibs\Convert\Byte::stringByteFormat($input, \CoreLibs\Convert\Byte::RETURN_AS_STRING) + ); + $this->assertEquals( + $expected_string_si, + \CoreLibs\Convert\Byte::stringByteFormat( + $input, + \CoreLibs\Convert\Byte::BYTE_FORMAT_SI | \CoreLibs\Convert\Byte::RETURN_AS_STRING + ) + ); } /** diff --git a/4dev/tests/Convert/CoreLibsConvertMathTest.php b/4dev/tests/Convert/CoreLibsConvertMathTest.php index 5df06aa0..c3aa145b 100644 --- a/4dev/tests/Convert/CoreLibsConvertMathTest.php +++ b/4dev/tests/Convert/CoreLibsConvertMathTest.php @@ -122,9 +122,9 @@ final class CoreLibsConvertMathTest extends TestCase public function providerCbrt(): array { return [ - 'cube root of 2' => [2, 1.25992, 5], - 'cube root of 3' => [3, 1.44225, 5], - 'cube root of -1' => [-1, 'NAN', 0], + 'cube root of 2' => [2, 1.25992, 5, null], + 'cube root of 3' => [3, 1.44225, 5, null], + 'cube root of -1' => [-1, 'NAN', 0, \InvalidArgumentException::class], ]; } @@ -138,10 +138,14 @@ final class CoreLibsConvertMathTest extends TestCase * @param float|int $number * @param float $expected * @param int $round_to + * @param ?string $exception * @return void */ - public function testCbrt(float|int $number, float|string $expected, int $round_to): void + public function testCbrt(float|int $number, float|string $expected, int $round_to, ?string $exception): void { + if ($exception !== null) { + $this->expectException($exception); + } $this->assertEquals( $expected, round(\CoreLibs\Convert\Math::cbrt($number), $round_to) diff --git a/4dev/tests/UrlRequests/CoreLibsUrlRequestsCurlTest.php b/4dev/tests/UrlRequests/CoreLibsUrlRequestsCurlTest.php index e5a22024..75c7a14c 100644 --- a/4dev/tests/UrlRequests/CoreLibsUrlRequestsCurlTest.php +++ b/4dev/tests/UrlRequests/CoreLibsUrlRequestsCurlTest.php @@ -59,8 +59,6 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase continue; } $this->url_basic = $url; - // split out the last / part for url set test - curl_close($handle); // print "Open: $url\n"; break; } @@ -969,76 +967,77 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase "query" => ["foo-get" => "bar"] ]); $this->assertEquals("200", $response["code"], "multi call: get response code not matching"); - if (PHP_VERSION_ID >= 80400) { - $this->assertEquals( - '{"HEADERS":{"HTTP_HOST":"soba.egplusww.jp",' - . '"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1","HTTP_FIRST_CALL":"get",' - . '"HTTP_ACCEPT":"*\/*"},"REQUEST_TYPE":"GET","PARAMS":{"foo-get":"bar"},"BODY":null}', - $response['content'], - 'multi call: get content not matching' - ); - } else { - $this->assertEquals( - '{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",' - . '"HTTP_FIRST_CALL":"get","HTTP_ACCEPT":"*\/*",' - . '"HTTP_HOST":"soba.egplusww.jp"},' - . '"REQUEST_TYPE":"GET",' - . '"PARAMS":{"foo-get":"bar"},"BODY":null}', - $response['content'], - 'multi call: get content not matching' - ); - } + $request_expected = json_decode( + <<assertEquals( + $request_expected, + json_decode($response['content'], true), + 'multi call: get content not matching' + ); // post $response = $curl->post($this->url_basic, [ "headers" => ["second-call" => "post"], "body" => ["foo-post" => "baz"] ]); $this->assertEquals("200", $response["code"], "multi call: post response code not matching"); - if (PHP_VERSION_ID >= 80400) { - $this->assertEquals( - '{"HEADERS":{"HTTP_HOST":"soba.egplusww.jp",' - . '"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",' - . '"HTTP_SECOND_CALL":"post","HTTP_ACCEPT":"*\/*"},' - . '"REQUEST_TYPE":"POST","PARAMS":[],"BODY":{"foo-post":"baz"}}', - $response['content'], - 'multi call: post content not matching' - ); - } else { - $this->assertEquals( - '{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",' - . '"HTTP_SECOND_CALL":"post","HTTP_ACCEPT":"*\/*",' - . '"HTTP_HOST":"soba.egplusww.jp"},' - . '"REQUEST_TYPE":"POST",' - . '"PARAMS":[],"BODY":{"foo-post":"baz"}}', - $response['content'], - 'multi call: post content not matching' - ); - } + $request_expected = json_decode( + <<assertEquals( + $request_expected, + json_decode($response['content'], true), + 'multi call: post content not matching' + ); // delete $response = $curl->delete($this->url_basic, [ "headers" => ["third-call" => "delete"], ]); $this->assertEquals("200", $response["code"], "multi call: delete response code not matching"); - if (PHP_VERSION_ID >= 80400) { - $this->assertEquals( - '{"HEADERS":{"HTTP_HOST":"soba.egplusww.jp",' - . '"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",' - . '"HTTP_THIRD_CALL":"delete","HTTP_ACCEPT":"*\/*"},' - . '"REQUEST_TYPE":"DELETE","PARAMS":[],"BODY":[]}', - $response['content'], - 'multi call: delete content not matching' - ); - } else { - $this->assertEquals( - '{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",' - . '"HTTP_THIRD_CALL":"delete","HTTP_ACCEPT":"*\/*",' - . '"HTTP_HOST":"soba.egplusww.jp"},' - . '"REQUEST_TYPE":"DELETE",' - . '"PARAMS":[],"BODY":[]}', - $response['content'], - 'multi call: delete content not matching' - ); - } + $request_expected = json_decode( + <<assertEquals( + $request_expected, + json_decode($response['content'], true), + 'multi call: delete content not matching' + ); } // MARK: auth header set via config diff --git a/www/admin/class_test.byte.php b/www/admin/class_test.byte.php index cc17ce2f..81577703 100644 --- a/www/admin/class_test.byte.php +++ b/www/admin/class_test.byte.php @@ -74,9 +74,21 @@ foreach ($bytes as $byte) { print '
'; print "(" . number_format($byte) . "/" . $byte . ") bytes :"; $_bytes = Byte::humanReadableByteFormat($byte); - print '
' . $_bytes; - print '
'; - print Byte::stringByteFormat($_bytes); + print '
'; + print '
' . $_bytes . '
'; + print '
'; + try { + print Byte::stringByteFormat($_bytes); + } catch (\LengthException $e) { + print "LengthException 1: " . $e->getMessage(); + try { + print "
S: " . Byte::stringByteFormat($_bytes, Byte::RETURN_AS_STRING); + } catch (\LengthException $e) { + print "LengthException 2: " . $e->getMessage(); + } catch (\RuntimeException $e) { + print "RuntimeException 1: " . $e->getMessage(); + } + } print "
"; // print ""; @@ -87,13 +99,85 @@ foreach ($bytes as $byte) { print "bytes [si]:"; $_bytes = Byte::humanReadableByteFormat($byte, Byte::BYTE_FORMAT_SI); print '
' . $_bytes; - print '
'; - print Byte::stringByteFormat($_bytes); + print '
'; + try { + print Byte::stringByteFormat($_bytes); + } catch (\LengthException $e) { + print "LengthException A: " . $e->getMessage(); + try { + print "
Ssi: " . Byte::stringByteFormat($_bytes, Byte::RETURN_AS_STRING | Byte::BYTE_FORMAT_SI); + } catch (\LengthException $e) { + print "LengthException B: " . $e->getMessage(); + } catch (\RuntimeException $e) { + print "RuntimeException A: " . $e->getMessage(); + } + } print "
"; // print ""; } +$string_bytes = [ + '-117.42 MB', + '242.98 MB', + '254.78 MiB', + '1 EiB', + '8 EB', + '867.36EB', + '1000EB', + '10000EB', +]; +print "BYTE STRING TO BYTES TESTS
"; +foreach ($string_bytes as $string) { + print '
'; + // + print '
'; + print "string byte ($string) to bytes :"; + try { + $_bytes = Byte::stringByteFormat($string); + } catch (\LengthException $e) { + print "
LengthException A: " . $e->getMessage(); + $_bytes = 0; + } + try { + $_bytes_string = Byte::stringByteFormat($string, Byte::RETURN_AS_STRING); + } catch (\LengthException $e) { + print "
LengthException B: " . $e->getMessage(); + $_bytes_string = ''; + } catch (\RuntimeException $e) { + print "
RuntimeException: " . $e->getMessage(); + $_bytes_string = ''; + } + try { + $_bytes_si = Byte::stringByteFormat($string, Byte::BYTE_FORMAT_SI); + } catch (\LengthException $e) { + print "
LengthException A: " . $e->getMessage(); + $_bytes_si = 0; + } + try { + $_bytes_string_si = Byte::stringByteFormat($string, Byte::RETURN_AS_STRING | Byte::BYTE_FORMAT_SI); + } catch (\LengthException $e) { + print "
LengthException B: " . $e->getMessage(); + $_bytes_string_si = ''; + } catch (\RuntimeException $e) { + print "
RuntimeException: " . $e->getMessage(); + $_bytes_string_si = ''; + } + print '
'; + print '
' + . "F:" . number_format((int)$_bytes) + . '
B: ' . $_bytes + . '
S: ' . $_bytes_string + . "
Fsi:" . number_format((int)$_bytes_si) + . '
Bsi: ' . $_bytes_si + . '
Ssi: ' . $_bytes_string_si; + print '
'; + print '
'; + print "B: " . Byte::humanReadableByteFormat($_bytes) . "
"; + print "Bsi: " . Byte::humanReadableByteFormat($_bytes_si, Byte::BYTE_FORMAT_SI); + print "
"; + print "
"; +} print ""; // __END__ diff --git a/www/lib/CoreLibs/Check/Encoding.php b/www/lib/CoreLibs/Check/Encoding.php index 08cb9dfb..b690c04d 100644 --- a/www/lib/CoreLibs/Check/Encoding.php +++ b/www/lib/CoreLibs/Check/Encoding.php @@ -114,7 +114,7 @@ class Encoding (($char != $r_char && (!self::$mb_error_char || in_array(self::$mb_error_char, ['none', 'long', 'entity']))) || ($char != $r_char && $r_char == self::$mb_error_char && self::$mb_error_char)) && - ord($char) != 194 + ord($char[0]) != 194 ) { $failed[] = $char; } diff --git a/www/lib/CoreLibs/Convert/Byte.php b/www/lib/CoreLibs/Convert/Byte.php index 22e9cbf5..a9ddbd99 100644 --- a/www/lib/CoreLibs/Convert/Byte.php +++ b/www/lib/CoreLibs/Convert/Byte.php @@ -14,6 +14,7 @@ class Byte public const BYTE_FORMAT_NOSPACE = 1; public const BYTE_FORMAT_ADJUST = 2; public const BYTE_FORMAT_SI = 4; + public const RETURN_AS_STRING = 8; /** * This function replaces the old byteStringFormat @@ -119,7 +120,9 @@ class Byte * @param int $flags bitwise flag with use space turned on * BYTE_FORMAT_SI: use 1000 instead of 1024 * @return string|int|float converted value or original value - * @throws \InvalidArgumentException 1: no valid flag set + * @throws \InvalidArgumentException no valid flag set + * @throws \LengthException number too large to convert to int + * @throws \RuntimeException BCMath extension not loaded if flag is set to string */ public static function stringByteFormat(string|int|float $number, int $flags = 0): string|int|float { @@ -129,7 +132,12 @@ class Byte } else { $si = false; } - if ($flags != 0 && $flags != 4) { + if ($flags & self::RETURN_AS_STRING) { + $return_as_string = true; + } else { + $return_as_string = false; + } + if ($flags != 0 && $flags != 4 && $flags != 8 && $flags != 12) { throw new \InvalidArgumentException("Invalid flags parameter: $flags", 1); } // matches in regex @@ -142,6 +150,10 @@ class Byte strtolower((string)$number), $matches ); + $number_negative = false; + if (!empty($matches[1])) { + $number_negative = true; + } if (isset($matches[2]) && isset($matches[3])) { // remove all non valid characters from the number $number = preg_replace('/[^0-9\.]/', '', $matches[2]); @@ -152,12 +164,48 @@ class Byte if ($unit) { $number = $number * pow($si ? 1000 : 1024, stripos($valid_units_, $unit[0]) ?: 0); } + // if the number is too large, we cannot convert to int directly + if ($number <= PHP_INT_MIN || $number >= PHP_INT_MAX) { + // if we do not want to convert to string + if (!$return_as_string) { + throw new \LengthException( + 'Number too large be converted to int: ' . (string)$number + ); + } + // for string, check if bcmath is loaded, if not this will not work + if (!extension_loaded('bcmath')) { + throw new \RuntimeException( + 'Number too large be converted to int and BCMath extension not loaded: ' . (string)$number + ); + } + } + // string return + if ($return_as_string) { + // return as string to avoid overflow + // $number = (string)round($number); + $number = bcmul(number_format( + $number, + 12, + '.', + '' + ), "1"); + if ($number_negative) { + $number = '-' . $number; + } + return $number; + } // convert to INT to avoid +E output $number = (int)round($number); // if negative input, keep nnegative - if (!empty($matches[1])) { + if ($number_negative) { $number *= -1; } + // check if number is negative but should be, this is Lenght overflow + if (!$number_negative && $number < 0) { + throw new \LengthException( + 'Number too large be converted to int: ' . (string)$number + ); + } } // if not matching return as is return $number; diff --git a/www/lib/CoreLibs/Convert/Math.php b/www/lib/CoreLibs/Convert/Math.php index 368c4854..6aba6f4b 100644 --- a/www/lib/CoreLibs/Convert/Math.php +++ b/www/lib/CoreLibs/Convert/Math.php @@ -62,10 +62,15 @@ class Math * * @param float $number Number to cubic root * @return float Calculated value + * @throws \InvalidArgumentException if $number is negative */ public static function cbrt(float|int $number): float { - return pow((float)$number, 1.0 / 3); + $value = pow((float)$number, 1.0 / 3); + if (is_nan($value)) { + throw new \InvalidArgumentException('cube root from this number is not supported: ' . $number); + } + return $value; } /** diff --git a/www/lib/CoreLibs/Logging/ErrorMessage.php b/www/lib/CoreLibs/Logging/ErrorMessage.php index bf99f972..153faf2c 100644 --- a/www/lib/CoreLibs/Logging/ErrorMessage.php +++ b/www/lib/CoreLibs/Logging/ErrorMessage.php @@ -291,7 +291,7 @@ class ErrorMessage */ public function getLastErrorMsg(): array { - return $this->error_str[array_key_last($this->error_str)] ?? [ + return $this->error_str[array_key_last($this->error_str) ?? -1] ?? [ 'level' => '', 'str' => '', 'id' => '', diff --git a/www/lib/CoreLibs/Output/Image.php b/www/lib/CoreLibs/Output/Image.php index aa449602..94d7f181 100644 --- a/www/lib/CoreLibs/Output/Image.php +++ b/www/lib/CoreLibs/Output/Image.php @@ -365,9 +365,6 @@ class Image imagepng($thumb, $thumbnail_write_path . $thumbnail); break; } - // free up resources (in case we are called in a loop) - imagedestroy($source); - imagedestroy($thumb); } else { throw new \RuntimeException( 'Invalid source image file. Only JPEG/PNG are allowed: ' . $filename, @@ -543,8 +540,6 @@ class Image imagepng($img, $filename); break; } - // clean up image if we have an image - imagedestroy($img); } } diff --git a/www/lib/CoreLibs/UrlRequests/Curl.php b/www/lib/CoreLibs/UrlRequests/Curl.php index 40b306f1..1af6318e 100644 --- a/www/lib/CoreLibs/UrlRequests/Curl.php +++ b/www/lib/CoreLibs/UrlRequests/Curl.php @@ -614,8 +614,6 @@ class Curl implements Interface\RequestsInterface // print "CURLINFO_HEADER_OUT:
" . curl_getinfo($handle, CURLINFO_HEADER_OUT) . "
"; // get response code and bail on not authorized $http_response = $this->handleCurlResponse($handle, $http_result, $options['http_errors']); - // close handler - $this->handleCurlClose($handle); // return response and result return [ 'code' => (string)$http_response, @@ -838,17 +836,6 @@ class Curl implements Interface\RequestsInterface ); } - /** - * close the current curl handle - * - * @param \CurlHandle $handle - * @return void - */ - private function handleCurlClose(\CurlHandle $handle): void - { - curl_close($handle); - } - // ********************************************************************* // MARK: PUBLIC METHODS // *********************************************************************