From ba5e78e839854ad34dd7033fe2f5c018cc41ae03 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Mon, 17 Feb 2025 11:25:36 +0900 Subject: [PATCH] Config errors throw exception, bug fixes for date interval, eslint update, Login ACL number to unit detail --- src/ACL/Login.php | 3 +- src/Admin/EditBase.php | 2 +- src/Combined/DateTime.php | 52 +++-- src/DB/IO.php | 9 +- .../AAASetupData/requests/http_requests.php | 4 +- .../Combined/CoreLibsCombinedDateTimeTest.php | 183 ++++++++++++++---- .../CoreLibsUrlRequestsCurlTest.php | 86 +++++--- 7 files changed, 252 insertions(+), 87 deletions(-) diff --git a/src/ACL/Login.php b/src/ACL/Login.php index 3eff991..890119f 100644 --- a/src/ACL/Login.php +++ b/src/ACL/Login.php @@ -1560,6 +1560,7 @@ class Login 'uid' => $unit['uid'], 'cuuid' => $unit['cuuid'], 'level' => $this->default_acl_list[$this->acl['unit'][$ea_cuid]]['name'] ?? -1, + 'level_number' => $this->acl['unit'][$ea_cuid], 'default' => $unit['default'], 'data' => $unit['data'], 'additional_acl' => $unit['additional_acl'] @@ -2538,7 +2539,7 @@ HTML; $this->login_user_id, -1, $login_user_id_changed - ); + ) ?? ''; // flag unclean input data if ($login_user_id_changed > 0) { $this->login_user_id_unclear = true; diff --git a/src/Admin/EditBase.php b/src/Admin/EditBase.php index 20312b5..754c33b 100644 --- a/src/Admin/EditBase.php +++ b/src/Admin/EditBase.php @@ -76,7 +76,7 @@ class EditBase ); if ($this->form->mobile_phone) { echo "I am sorry, but this page cannot be viewed by a mobile phone"; - exit; + exit(1); } // $this->log->debug('POST', $this->log->prAr($_POST)); } diff --git a/src/Combined/DateTime.php b/src/Combined/DateTime.php index d46619c..d123c53 100644 --- a/src/Combined/DateTime.php +++ b/src/Combined/DateTime.php @@ -639,16 +639,26 @@ class DateTime * * @param string $start_date valid start date (y/m/d) * @param string $end_date valid end date (y/m/d) - * @param bool $return_named return array type, false (default), true for named - * @return array 0/overall, 1/weekday, 2/weekend + * @param bool $return_named [default=false] return array type, false (default), true for named + * @param bool $include_end_date [default=true] include end date in calc + * @param bool $exclude_start_date [default=false] include end date in calc + * @return array{0:int,1:int,2:int,3:bool}|array{overall:int,weekday:int,weekend:int,reverse:bool} + * 0/overall, 1/weekday, 2/weekend, 3/reverse */ public static function calcDaysInterval( string $start_date, string $end_date, - bool $return_named = false + bool $return_named = false, + bool $include_end_date = true, + bool $exclude_start_date = false ): array { // pos 0 all, pos 1 weekday, pos 2 weekend - $days = []; + $days = [ + 0 => 0, + 1 => 0, + 2 => 0, + 3 => false, + ]; // if anything invalid, return 0,0,0 try { $start = new \DateTime($start_date); @@ -659,19 +669,30 @@ class DateTime 'overall' => 0, 'weekday' => 0, 'weekend' => 0, + 'reverse' => false ]; } else { - return [0, 0, 0]; + return $days; } } // so we include the last day too, we need to add +1 second in the time - $end->setTime(0, 0, 1); - // if end date before start date, only this will be filled - $days[0] = $end->diff($start)->days; - $days[1] = 0; - $days[2] = 0; + // if start is before end, switch dates and flag + $days[3] = false; + if ($start > $end) { + $new_start = $end; + $end = $start; + $start = $new_start; + $days[3] = true; + } // get period for weekends/weekdays - $period = new \DatePeriod($start, new \DateInterval('P1D'), $end); + $options = 0; + if ($include_end_date) { + $options |= \DatePeriod::INCLUDE_END_DATE; + } + if ($exclude_start_date) { + $options |= \DatePeriod::EXCLUDE_START_DATE; + } + $period = new \DatePeriod($start, new \DateInterval('P1D'), $end, $options); foreach ($period as $dt) { $curr = $dt->format('D'); if ($curr == 'Sat' || $curr == 'Sun') { @@ -679,12 +700,14 @@ class DateTime } else { $days[1]++; } + $days[0]++; } if ($return_named === true) { return [ 'overall' => $days[0], 'weekday' => $days[1], 'weekend' => $days[2], + 'reverse' => $days[3], ]; } else { return $days; @@ -705,6 +728,13 @@ class DateTime ): bool { $dd_start = new \DateTime($start_date); $dd_end = new \DateTime($end_date); + // flip if start is after end + if ($dd_start > $dd_end) { + $new_start = $dd_end; + $dd_end = $dd_start; + $dd_start = $new_start; + } + // if start > end, flip if ( // starts with a weekend $dd_start->format('N') >= 6 || diff --git a/src/DB/IO.php b/src/DB/IO.php index 038f32d..4720632 100644 --- a/src/DB/IO.php +++ b/src/DB/IO.php @@ -500,7 +500,7 @@ class IO die(''); } // write to internal one, once OK - $this->db_functions = $db_functions; + $this->db_functions = $db_functions; /** @phan-suppress-current-line PhanPossiblyNullTypeMismatchProperty */ // connect to DB if (!$this->__connectToDB()) { @@ -1413,10 +1413,7 @@ class IO $this->pk_name_table[$table] ? $this->pk_name_table[$table] : 'NULL'; } - if ( - !preg_match(self::REGEX_RETURNING, $this->query) && - $this->pk_name && $this->pk_name != 'NULL' - ) { + if (!preg_match(self::REGEX_RETURNING, $this->query) && $this->pk_name != 'NULL') { // check if this query has a ; at the end and remove it $__query = preg_replace("/(;\s*)$/", '', $this->query); // must be query, if preg replace failed, use query as before @@ -1426,7 +1423,7 @@ class IO } elseif ( preg_match(self::REGEX_RETURNING, $this->query, $matches) ) { - if ($this->pk_name && $this->pk_name != 'NULL') { + if ($this->pk_name != 'NULL') { // add the primary key if it is not in the returning set if (!preg_match("/$this->pk_name/", $matches[1])) { $this->query .= " , " . $this->pk_name; diff --git a/test/phpunit/AAASetupData/requests/http_requests.php b/test/phpunit/AAASetupData/requests/http_requests.php index 912d715..9523a60 100644 --- a/test/phpunit/AAASetupData/requests/http_requests.php +++ b/test/phpunit/AAASetupData/requests/http_requests.php @@ -48,7 +48,7 @@ header("Content-Type: application/json; charset=UTF-8"); if (!empty($http_headers['HTTP_AUTHORIZATION']) && !empty($http_headers['HTTP_RUNAUTHTEST'])) { header("HTTP/1.1 401 Unauthorized"); print buildContent($http_headers, '{"code": 401, "content": {"Error": "Not Authorized"}}'); - exit; + exit(1); } // if server request type is get set file_get to null -> no body @@ -57,7 +57,7 @@ if ($_SERVER['REQUEST_METHOD'] == "GET") { } elseif (($file_get = file_get_contents('php://input')) === false) { header("HTTP/1.1 404 Not Found"); print buildContent($http_headers, '{"code": 404, "content": {"Error": "file_get_contents failed"}}'); - exit; + exit(1); } print buildContent($http_headers, $file_get); diff --git a/test/phpunit/Combined/CoreLibsCombinedDateTimeTest.php b/test/phpunit/Combined/CoreLibsCombinedDateTimeTest.php index d7efa9b..58040c4 100644 --- a/test/phpunit/Combined/CoreLibsCombinedDateTimeTest.php +++ b/test/phpunit/Combined/CoreLibsCombinedDateTimeTest.php @@ -926,48 +926,114 @@ final class CoreLibsCombinedDateTimeTest extends TestCase public function daysIntervalProvider(): array { return [ - 'valid interval /, not named array' => [ - '2020/1/1', - '2020/1/30', - false, - [29, 22, 8], + // normal and format tests + 'valid interval / not named array' => [ + 'input_a' => '2020/1/1', + 'input_b' => '2020/1/30', + 'return_named' => false, // return_named + 'include_end_date' => true, // include_end_date + 'exclude_start_date' => false, // exclude_start_date + 'expected' => [30, 22, 8, false], ], - 'valid interval /, named array' => [ - '2020/1/1', - '2020/1/30', - true, - ['overall' => 29, 'weekday' => 22, 'weekend' => 8], + 'valid interval / named array' => [ + 'input_a' => '2020/1/1', + 'input_b' => '2020/1/30', + 'return_named' => true, + 'include_end_date' => true, + 'exclude_start_date' => false, + 'expected' => ['overall' => 30, 'weekday' => 22, 'weekend' => 8, 'reverse' => false], ], - 'valid interval -' => [ - '2020-1-1', - '2020-1-30', - false, - [29, 22, 8], - ], - 'valid interval switched' => [ - '2020/1/30', - '2020/1/1', - false, - [28, 0, 0], + 'valid interval with "-"' => [ + 'input_a' => '2020-1-1', + 'input_b' => '2020-1-30', + 'return_named' => false, + 'include_end_date' => true, + 'exclude_start_date' => false, + 'expected' => [30, 22, 8, false], ], 'valid interval with time' => [ - '2020/1/1 12:12:12', - '2020/1/30 13:13:13', - false, - [28, 21, 8], + 'input_a' => '2020/1/1 12:12:12', + 'input_b' => '2020/1/30 13:13:13', + 'return_named' => false, + 'include_end_date' => true, + 'exclude_start_date' => false, + 'expected' => [30, 22, 8, false], ], + // invalid 'invalid dates' => [ - 'abc', - 'xyz', - false, - [0, 0, 0] + 'input_a' => 'abc', + 'input_b' => 'xyz', + 'return_named' => false, + 'include_end_date' => true, + 'exclude_start_date' => false, + 'expected' => [0, 0, 0, false] ], - // this test will take a long imte + // this test will take a long time 'out of bound dates' => [ - '1900-1-1', - '9999-12-31', - false, - [2958463,2113189,845274], + 'input_a' => '1900-1-1', + 'input_b' => '9999-12-31', + 'return_named' => false, + 'include_end_date' => true, + 'exclude_start_date' => false, + 'expected' => [2958463, 2113189, 845274, false], + ], + // tests for include/exclude + 'exclude end date' => [ + 'input_b' => '2020/1/1', + 'input_a' => '2020/1/30', + 'return_named' => false, + 'include_end_date' => false, + 'exclude_start_date' => false, + 'expected' => [29, 21, 8, false], + ], + 'exclude start date' => [ + 'input_b' => '2020/1/1', + 'input_a' => '2020/1/30', + 'return_named' => false, + 'include_end_date' => true, + 'exclude_start_date' => true, + 'expected' => [29, 21, 8, false], + ], + 'exclude start and end date' => [ + 'input_b' => '2020/1/1', + 'input_a' => '2020/1/30', + 'return_named' => false, + 'include_end_date' => false, + 'exclude_start_date' => true, + 'expected' => [28, 20, 8, false], + ], + // reverse + 'reverse: valid interval' => [ + 'input_a' => '2020/1/30', + 'input_b' => '2020/1/1', + 'return_named' => false, + 'include_end_date' => true, + 'exclude_start_date' => false, + 'expected' => [30, 22, 8, true], + ], + 'reverse: exclude end date' => [ + 'input_a' => '2020/1/30', + 'input_b' => '2020/1/1', + 'return_named' => false, + 'include_end_date' => false, + 'exclude_start_date' => false, + 'expected' => [29, 21, 8, true], + ], + 'reverse: exclude start date' => [ + 'input_a' => '2020/1/30', + 'input_b' => '2020/1/1', + 'return_named' => false, + 'include_end_date' => true, + 'exclude_start_date' => true, + 'expected' => [29, 21, 8, true], + ], + 'reverse: exclude start and end date' => [ + 'input_a' => '2020/1/30', + 'input_b' => '2020/1/1', + 'return_named' => false, + 'include_end_date' => false, + 'exclude_start_date' => true, + 'expected' => [28, 20, 8, true], ], ]; } @@ -982,19 +1048,27 @@ final class CoreLibsCombinedDateTimeTest extends TestCase * * @param string $input_a * @param string $input_b - * @param bool $flag - * @param array $expected + * @param bool $return_named + * @param array $expected * @return void */ public function testCalcDaysInterval( string $input_a, string $input_b, - bool $flag, + bool $return_named, + bool $include_end_date, + bool $exclude_start_date, $expected ): void { $this->assertEquals( $expected, - \CoreLibs\Combined\DateTime::calcDaysInterval($input_a, $input_b, $flag) + \CoreLibs\Combined\DateTime::calcDaysInterval( + $input_a, + $input_b, + return_named:$return_named, + include_end_date:$include_end_date, + exclude_start_date:$exclude_start_date + ) ); } @@ -1187,7 +1261,38 @@ final class CoreLibsCombinedDateTimeTest extends TestCase '2023-07-03', '2023-07-27', true - ] + ], + // reverse + 'reverse: no weekend' => [ + '2023-07-04', + '2023-07-03', + false + ], + 'reverse: start weekend sat' => [ + '2023-07-04', + '2023-07-01', + true + ], + 'reverse: start weekend sun' => [ + '2023-07-04', + '2023-07-02', + true + ], + 'reverse: end weekend sat' => [ + '2023-07-08', + '2023-07-03', + true + ], + 'reverse: end weekend sun' => [ + '2023-07-09', + '2023-07-03', + true + ], + 'reverse: long period > 6 days' => [ + '2023-07-27', + '2023-07-03', + true + ], ]; } diff --git a/test/phpunit/UrlRequests/CoreLibsUrlRequestsCurlTest.php b/test/phpunit/UrlRequests/CoreLibsUrlRequestsCurlTest.php index 276b2ef..e5a2202 100644 --- a/test/phpunit/UrlRequests/CoreLibsUrlRequestsCurlTest.php +++ b/test/phpunit/UrlRequests/CoreLibsUrlRequestsCurlTest.php @@ -969,44 +969,76 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase "query" => ["foo-get" => "bar"] ]); $this->assertEquals("200", $response["code"], "multi call: get response code not matching"); - $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' - ); + 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' + ); + } // 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"); - $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' - ); + 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' + ); + } // delete $response = $curl->delete($this->url_basic, [ "headers" => ["third-call" => "delete"], ]); $this->assertEquals("200", $response["code"], "multi call: delete response code not matching"); - $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' - ); + 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' + ); + } } // MARK: auth header set via config