From 899fe28767946af8f46397d669013a0312e9ceca Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Thu, 7 Nov 2024 22:30:36 +0700 Subject: [PATCH 01/12] Update Area.php --- src/Geometry/Area.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Geometry/Area.php b/src/Geometry/Area.php index a0ffff4..bbe1910 100644 --- a/src/Geometry/Area.php +++ b/src/Geometry/Area.php @@ -172,12 +172,12 @@ public function getHTML() $attrs[] = 'coords="' . implode(", ", $this->getCoords($this->zoom)) . '"'; if (isset($this->href)) { - $attrs[] = 'href="' . htmlspecialchars($this->href) . '"'; + $attrs[] = 'href="' . $this->href . '"'; } if (isset($this->attributes) && is_array($this->attributes)) { foreach ($this->attributes as $key => $value) { - $attrs[] = $key . '="' . htmlspecialchars($value) . '"'; + $attrs[] = $key . '="' . $value . '"'; } } From 2bc7560fc58a5cd483079857595f2b318fdd6442 Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Fri, 8 Nov 2024 08:12:46 +0700 Subject: [PATCH 02/12] Add resumable file downloader --- src/File/PicoDownloadFile.php | 131 ++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 src/File/PicoDownloadFile.php diff --git a/src/File/PicoDownloadFile.php b/src/File/PicoDownloadFile.php new file mode 100644 index 0000000..70c1951 --- /dev/null +++ b/src/File/PicoDownloadFile.php @@ -0,0 +1,131 @@ +download(); + * ``` + * + * @package MagicObject\File + * @author Kamshory + * @link https://github.com/Planetbiru/MagicApp + */ +class PicoDownloadFile +{ + /** + * The file path to the file being downloaded. + * + * @var string + */ + private $file; + + /** + * PicoDownloadFile constructor. + * + * Initializes the class with the file path to be downloaded. + * + * @param string $file The file path of the file to be downloaded. + */ + public function __construct($file) + { + $this->file = $file; + } + + /** + * Initiates the download of the file. + * + * This method handles the HTTP headers for a proper file download, including partial content support + * if the client requests a specific range. It will read the requested file and send it in chunks to the client. + * If the file does not exist, it will return a 404 response. If an invalid range is requested, a 416 error will + * be sent. If everything is valid, the file will be served to the client. + * + * **Process**: + * - Verifies that the file exists. + * - Parses and handles byte range requests if provided by the client. + * - Sends the appropriate HTTP headers for partial or full file downloads. + * - Streams the file in chunks to the client. + * + * @return void + */ + public function download() + { + // Ensure the file exists + if (!file_exists($this->file)) { + header("HTTP/1.1 404 Not Found"); + echo "File not found."; + exit; + } + + // Get file size + $fileSize = filesize($this->file); + + // Handle Range requests from the client + if (isset($_SERVER['HTTP_RANGE'])) { + // Format Range: bytes=start-end + list($range, $extra) = explode(',', $_SERVER['HTTP_RANGE'], 2); + list($start, $end) = explode('-', $range); + $start = (int) $start; + $end = $end ? (int) $end : $fileSize - 1; + } else { + // If no range is provided, send the entire file + $start = 0; + $end = $fileSize - 1; + } + + // Ensure the range is valid + if ($start > $end || $start >= $fileSize || $end >= $fileSize) { + header("HTTP/1.1 416 Range Not Satisfiable"); + header("Content-Range: bytes 0-0/$fileSize"); + exit; + } + + // Send response headers for byte-range support + header('HTTP/1.1 206 Partial Content'); + header("Content-Type: application/octet-stream"); + header("Content-Description: File Transfer"); + header("Content-Disposition: attachment; filename=\"" . basename($this->file) . "\""); + header("Content-Range: bytes $start-$end/$fileSize"); + header("Content-Length: " . ($end - $start + 1)); + header("Accept-Ranges: bytes"); + + // Open the file for reading + $fp = fopen($this->file, 'rb'); + if ($fp === false) { + header("HTTP/1.1 500 Internal Server Error"); + echo "Failed to open file."; + exit; + } + + // Set the file pointer to the requested start position + fseek($fp, $start); + + // Read and send the requested file chunk by chunk + $bufferSize = 1024 * 8; // 8 KB buffer size, adjustable + while (!feof($fp) && ftell($fp) <= $end) { + echo fread($fp, $bufferSize); + flush(); + } + + fclose($fp); + exit; + } +} From c6609e7866b28be2b71c927eaabd1bc310969dd6 Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Fri, 8 Nov 2024 08:18:51 +0700 Subject: [PATCH 03/12] Update PicoDownloadFile.php --- src/File/PicoDownloadFile.php | 85 ++++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 32 deletions(-) diff --git a/src/File/PicoDownloadFile.php b/src/File/PicoDownloadFile.php index 70c1951..e2fef7e 100644 --- a/src/File/PicoDownloadFile.php +++ b/src/File/PicoDownloadFile.php @@ -5,19 +5,20 @@ /** * Class PicoDownloadFile * - * This class handles the download of a file, supporting partial content requests (range requests). - * It allows a file to be served to the client in chunks, ensuring that large files can be downloaded - * without requiring the entire file to be loaded into memory. It also checks if the requested file exists - * and handles potential errors appropriately. + * This class facilitates downloading a file, supporting partial content requests (range requests), + * which allows the file to be transferred in chunks. This is particularly useful for large files + * where downloading the entire file in one go might be inefficient or unfeasible. The class also + * ensures that the requested file exists and handles various errors such as missing files and invalid + * range requests gracefully. * * **Key Features**: - * - Supports downloading entire files or partial file ranges. - * - Provides proper HTTP headers for file transfer and range requests. - * - Handles errors such as missing files or invalid range requests. + * - Supports downloading the full file or a partial file range (e.g., for resuming interrupted downloads). + * - Sends proper HTTP headers for file transfer and range requests. + * - Handles errors such as non-existent files or invalid range requests (HTTP 404 and 416). * * **Usage**: - * - Instantiate the class with the file path as a parameter. - * - Call the `download()` method to initiate the download process. + * - Instantiate the class with the file path and an optional filename parameter. + * - Call the `download()` method to trigger the file download. * * Example: * ```php @@ -32,36 +33,56 @@ class PicoDownloadFile { /** - * The file path to the file being downloaded. + * The path to the file being downloaded. * * @var string */ - private $file; + private $filepath; + + /** + * The name of the file to be sent to the client. If not provided, the filename will be + * inferred from the file's path. + * + * @var string + */ + private $filename; /** * PicoDownloadFile constructor. * - * Initializes the class with the file path to be downloaded. + * Initializes the class with the file path to be downloaded and an optional filename. + * If no filename is provided, the class will use the base name of the file path. * - * @param string $file The file path of the file to be downloaded. + * @param string $filepath The full path to the file to be downloaded. + * @param string|null $filename The name of the file to be sent in the download response (optional). */ - public function __construct($file) + public function __construct($filepath, $filename = null) { - $this->file = $file; + $this->filepath = $filepath; + // If no filename is provided, use the basename of the file path + if (!isset($filename)) { + $filename = basename($filepath); + } + $this->filename = $filename; } /** * Initiates the download of the file. * - * This method handles the HTTP headers for a proper file download, including partial content support - * if the client requests a specific range. It will read the requested file and send it in chunks to the client. - * If the file does not exist, it will return a 404 response. If an invalid range is requested, a 416 error will - * be sent. If everything is valid, the file will be served to the client. + * This method sends the appropriate HTTP headers to facilitate the download of a file, + * including support for partial content (range requests). The method handles: + * - Verifying the file's existence. + * - Parsing and handling byte range requests (for resuming downloads). + * - Sending appropriate HTTP headers for file transfer. + * - Streaming the file to the client in chunks (if applicable). + * + * If the file doesn't exist, a 404 error is sent. If the range is invalid, a 416 error is sent. + * If the file exists and everything is valid, the file will be served to the client. * * **Process**: - * - Verifies that the file exists. - * - Parses and handles byte range requests if provided by the client. - * - Sends the appropriate HTTP headers for partial or full file downloads. + * - Verifies that the file exists at the provided path. + * - Parses the range header (if any) and determines the appropriate byte range. + * - Sends headers for partial or full content transfer. * - Streams the file in chunks to the client. * * @return void @@ -69,16 +90,16 @@ public function __construct($file) public function download() { // Ensure the file exists - if (!file_exists($this->file)) { + if (!file_exists($this->filepath)) { header("HTTP/1.1 404 Not Found"); echo "File not found."; exit; } - // Get file size - $fileSize = filesize($this->file); + // Get the file size + $fileSize = filesize($this->filepath); - // Handle Range requests from the client + // Handle range requests if provided by the client if (isset($_SERVER['HTTP_RANGE'])) { // Format Range: bytes=start-end list($range, $extra) = explode(',', $_SERVER['HTTP_RANGE'], 2); @@ -98,28 +119,28 @@ public function download() exit; } - // Send response headers for byte-range support + // Send response headers for partial content (range requests) header('HTTP/1.1 206 Partial Content'); header("Content-Type: application/octet-stream"); header("Content-Description: File Transfer"); - header("Content-Disposition: attachment; filename=\"" . basename($this->file) . "\""); + header("Content-Disposition: attachment; filename=\"" . $this->filename . "\""); header("Content-Range: bytes $start-$end/$fileSize"); header("Content-Length: " . ($end - $start + 1)); header("Accept-Ranges: bytes"); // Open the file for reading - $fp = fopen($this->file, 'rb'); + $fp = fopen($this->filepath, 'rb'); if ($fp === false) { header("HTTP/1.1 500 Internal Server Error"); echo "Failed to open file."; exit; } - // Set the file pointer to the requested start position + // Move the pointer to the start position fseek($fp, $start); - // Read and send the requested file chunk by chunk - $bufferSize = 1024 * 8; // 8 KB buffer size, adjustable + // Read and send the file in chunks (8 KB buffer size) + $bufferSize = 1024 * 8; // 8 KB buffer size while (!feof($fp) && ftell($fp) <= $end) { echo fread($fp, $bufferSize); flush(); From a3bc8406a4dc9a62c48887f86145aaed35b6e6a8 Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Fri, 8 Nov 2024 08:19:16 +0700 Subject: [PATCH 04/12] Update PicoDownloadFile.php --- src/File/PicoDownloadFile.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/File/PicoDownloadFile.php b/src/File/PicoDownloadFile.php index e2fef7e..c2d4eda 100644 --- a/src/File/PicoDownloadFile.php +++ b/src/File/PicoDownloadFile.php @@ -102,7 +102,7 @@ public function download() // Handle range requests if provided by the client if (isset($_SERVER['HTTP_RANGE'])) { // Format Range: bytes=start-end - list($range, $extra) = explode(',', $_SERVER['HTTP_RANGE'], 2); + list($range, $extra) = explode(',', $_SERVER['HTTP_RANGE'], 2); //NOSONAR list($start, $end) = explode('-', $range); $start = (int) $start; $end = $end ? (int) $end : $fileSize - 1; From cc47d8694c1ee36ae66bb869db767c1a58e2b56e Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Fri, 8 Nov 2024 09:19:39 +0700 Subject: [PATCH 05/12] Refactor PicoDownloadFile --- src/File/PicoDownloadFile.php | 206 +++++++++++++++++++--------------- 1 file changed, 117 insertions(+), 89 deletions(-) diff --git a/src/File/PicoDownloadFile.php b/src/File/PicoDownloadFile.php index c2d4eda..18daa7e 100644 --- a/src/File/PicoDownloadFile.php +++ b/src/File/PicoDownloadFile.php @@ -5,121 +5,153 @@ /** * Class PicoDownloadFile * - * This class facilitates downloading a file, supporting partial content requests (range requests), - * which allows the file to be transferred in chunks. This is particularly useful for large files - * where downloading the entire file in one go might be inefficient or unfeasible. The class also - * ensures that the requested file exists and handles various errors such as missing files and invalid - * range requests gracefully. + * Facilitates downloading a file, with support for partial content (range requests). + * This class ensures that requested files exist, handles errors, and supports downloading large files + * efficiently by sending them in chunks. * - * **Key Features**: - * - Supports downloading the full file or a partial file range (e.g., for resuming interrupted downloads). - * - Sends proper HTTP headers for file transfer and range requests. - * - Handles errors such as non-existent files or invalid range requests (HTTP 404 and 416). - * - * **Usage**: - * - Instantiate the class with the file path and an optional filename parameter. - * - Call the `download()` method to trigger the file download. - * - * Example: - * ```php - * $file = new PicoDownloadFile('/path/to/file.zip'); - * $file->download(); - * ``` - * * @package MagicObject\File - * @author Kamshory - * @link https://github.com/Planetbiru/MagicApp */ class PicoDownloadFile { /** - * The path to the file being downloaded. - * - * @var string + * @var string The path to the file being downloaded. */ private $filepath; /** - * The name of the file to be sent to the client. If not provided, the filename will be - * inferred from the file's path. - * - * @var string + * @var string The filename to be used in the download response. */ private $filename; /** * PicoDownloadFile constructor. * - * Initializes the class with the file path to be downloaded and an optional filename. - * If no filename is provided, the class will use the base name of the file path. - * - * @param string $filepath The full path to the file to be downloaded. - * @param string|null $filename The name of the file to be sent in the download response (optional). + * @param string $filepath The full path to the file. + * @param string|null $filename The name of the file for download (optional). */ public function __construct($filepath, $filename = null) { $this->filepath = $filepath; - // If no filename is provided, use the basename of the file path - if (!isset($filename)) { - $filename = basename($filepath); - } - $this->filename = $filename; + $this->filename = $filename ?: basename($filepath); // Use basename if no filename provided } /** - * Initiates the download of the file. + * Initiates the download of the file with support for partial content (range requests). * - * This method sends the appropriate HTTP headers to facilitate the download of a file, - * including support for partial content (range requests). The method handles: - * - Verifying the file's existence. - * - Parsing and handling byte range requests (for resuming downloads). - * - Sending appropriate HTTP headers for file transfer. - * - Streaming the file to the client in chunks (if applicable). - * - * If the file doesn't exist, a 404 error is sent. If the range is invalid, a 416 error is sent. - * If the file exists and everything is valid, the file will be served to the client. + * Handles the following: + * - Verifies the file exists at the specified path. + * - Supports byte range requests for resuming downloads. + * - Sends appropriate HTTP headers for file transfer. + * - Streams the file to the client in chunks (8 KB by default). * - * **Process**: - * - Verifies that the file exists at the provided path. - * - Parses the range header (if any) and determines the appropriate byte range. - * - Sends headers for partial or full content transfer. - * - Streams the file in chunks to the client. + * @param bool $exit Whether to terminate the script after sending the file. Default is `false`. * - * @return void + * @return bool Returns `true` if the entire file was successfully sent, `false` if only part of the file was sent. */ - public function download() + public function download($exit = false) //NOSONAR { - // Ensure the file exists - if (!file_exists($this->filepath)) { - header("HTTP/1.1 404 Not Found"); - echo "File not found."; - exit; + if (!$this->fileExists()) { + $this->sendError(404, "File not found."); + return false; } - // Get the file size $fileSize = filesize($this->filepath); + list($start, $end) = $this->getRange($fileSize); + + if ($this->isInvalidRange($start, $end, $fileSize)) { + $this->sendError(416, "Range Not Satisfiable", $fileSize); + return false; + } - // Handle range requests if provided by the client + $this->sendHeaders($start, $end, $fileSize); + + $fp = fopen($this->filepath, 'rb'); + if ($fp === false) { + $this->sendError(500, "Failed to open file."); + return false; + } + + + $this->streamFile($fp, $start, $end); + + fclose($fp); + + if ($exit) { + exit; + } + + return $end === ($fileSize - 1); // Return true if the whole file was sent + } + + /** + * Checks if the file exists. + * + * @return bool True if the file exists, false otherwise. + */ + private function fileExists() + { + return file_exists($this->filepath); + } + + /** + * Sends an error response with the given status code and message. + * + * @param int $statusCode The HTTP status code. + * @param string $message The error message. + * @param int|null $fileSize The file size to include in the Content-Range header (optional). + */ + private function sendError($statusCode, $message, $fileSize = null) + { + header("HTTP/1.1 $statusCode $message"); + if ($fileSize !== null) { + header("Content-Range: bytes 0-0/$fileSize"); + } + echo $message; + } + + /** + * Determines the byte range from the HTTP_RANGE header. + * + * @param int $fileSize The size of the file. + * @return array The start and end byte positions for the range. + */ + private function getRange($fileSize) + { if (isset($_SERVER['HTTP_RANGE'])) { - // Format Range: bytes=start-end list($range, $extra) = explode(',', $_SERVER['HTTP_RANGE'], 2); //NOSONAR list($start, $end) = explode('-', $range); - $start = (int) $start; - $end = $end ? (int) $end : $fileSize - 1; + $start = max(0, (int)$start); + $end = $end ? (int)$end : $fileSize - 1; } else { - // If no range is provided, send the entire file $start = 0; $end = $fileSize - 1; } - // Ensure the range is valid - if ($start > $end || $start >= $fileSize || $end >= $fileSize) { - header("HTTP/1.1 416 Range Not Satisfiable"); - header("Content-Range: bytes 0-0/$fileSize"); - exit; - } + return [$start, $end]; + } - // Send response headers for partial content (range requests) + /** + * Checks if the byte range is valid. + * + * @param int $start The start byte. + * @param int $end The end byte. + * @param int $fileSize The total size of the file. + * @return bool True if the range is invalid. + */ + private function isInvalidRange($start, $end, $fileSize) + { + return $start > $end || $start >= $fileSize || $end >= $fileSize; + } + + /** + * Sends the appropriate HTTP headers for the download. + * + * @param int $start The start byte. + * @param int $end The end byte. + * @param int $fileSize The total size of the file. + */ + private function sendHeaders($start, $end, $fileSize) + { header('HTTP/1.1 206 Partial Content'); header("Content-Type: application/octet-stream"); header("Content-Description: File Transfer"); @@ -127,26 +159,22 @@ public function download() header("Content-Range: bytes $start-$end/$fileSize"); header("Content-Length: " . ($end - $start + 1)); header("Accept-Ranges: bytes"); + } - // Open the file for reading - $fp = fopen($this->filepath, 'rb'); - if ($fp === false) { - header("HTTP/1.1 500 Internal Server Error"); - echo "Failed to open file."; - exit; - } - - // Move the pointer to the start position - fseek($fp, $start); - - // Read and send the file in chunks (8 KB buffer size) + /** + * Streams the file to the client in chunks. + * + * @param resource $fp The file pointer. + * @param int $start The start byte. + * @param int $end The end byte. + */ + private function streamFile($fp, $start, $end) + { $bufferSize = 1024 * 8; // 8 KB buffer size + fseek($fp, $start); while (!feof($fp) && ftell($fp) <= $end) { echo fread($fp, $bufferSize); flush(); } - - fclose($fp); - exit; } } From 013329f6190a19b3f26e3b63e678bbe109c270ff Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Fri, 8 Nov 2024 09:22:18 +0700 Subject: [PATCH 06/12] Update PicoDownloadFile.php --- src/File/PicoDownloadFile.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/File/PicoDownloadFile.php b/src/File/PicoDownloadFile.php index 18daa7e..d59eb83 100644 --- a/src/File/PicoDownloadFile.php +++ b/src/File/PicoDownloadFile.php @@ -9,7 +9,9 @@ * This class ensures that requested files exist, handles errors, and supports downloading large files * efficiently by sending them in chunks. * + * @author Kamshory * @package MagicObject\File + * @link https://github.com/Planetbiru/MagicObject */ class PicoDownloadFile { @@ -70,7 +72,6 @@ public function download($exit = false) //NOSONAR $this->sendError(500, "Failed to open file."); return false; } - $this->streamFile($fp, $start, $end); From 64b0e5adcfc7c87031b6f48fac286daf8cb7029b Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Fri, 8 Nov 2024 09:41:15 +0700 Subject: [PATCH 07/12] Fix codesmell --- src/Util/PicoParsedown.php | 183 ++++++++++++++++++------------------- 1 file changed, 89 insertions(+), 94 deletions(-) diff --git a/src/Util/PicoParsedown.php b/src/Util/PicoParsedown.php index ca96107..6ac9a21 100644 --- a/src/Util/PicoParsedown.php +++ b/src/Util/PicoParsedown.php @@ -15,7 +15,7 @@ # # -class PicoParsedown +class PicoParsedown //NOSONAR { /** * The current version of the parser. @@ -206,7 +206,7 @@ public function setSafeMode($safeMode) * @param array $lines The lines of text to process. * @return string The generated HTML markup. */ - protected function lines(array $lines) + protected function lines(array $lines) //NOSONAR { $CurrentBlock = null; @@ -383,13 +383,13 @@ protected function isBlockCompletable($Type) protected function blockCode($line, $block = null) { if (isset($block) && !isset($block['type']) && !isset($block['interrupted'])) { - return; + return null; } if ($line['indent'] >= 4) { $text = substr($line['body'], 4); - $block = array( + return array( 'element' => array( 'name' => 'pre', 'handler' => 'element', @@ -400,7 +400,6 @@ protected function blockCode($line, $block = null) ), ); - return $block; } } @@ -440,7 +439,25 @@ protected function blockCodeContinue($line, $block) * @param array $block The block to complete. * @return array The completed code block. */ - protected function blockCodeComplete($block) + protected function blockCodeComplete($block) //NOSONAR + { + $text = $block['element']['text']['text']; + + $block['element']['text']['text'] = $text; + + return $block; + } + + /** + * Continue processing a fenced code block. + * + * This method appends additional lines to an existing fenced code block. + * + * @param array $line The line of text to process. + * @param array $block The current fenced code block being processed. + * @return array|null The updated fenced code block or null if not applicable. + */ + protected function blockFencedCodeComplete($block) //NOSONAR { $text = $block['element']['text']['text']; @@ -460,7 +477,7 @@ protected function blockCodeComplete($block) protected function blockComment($line) { if ($this->markupEscaped || $this->safeMode) { - return; + return null; } if (isset($line['text'][3]) && $line['text'][3] === '-' && $line['text'][2] === '-' && $line['text'][1] === '!') { @@ -488,7 +505,7 @@ protected function blockComment($line) protected function blockCommentContinue($line, array $block) { if (isset($block['closed'])) { - return; + return null; } $block['markup'] .= "\n" . $line['body']; @@ -539,7 +556,7 @@ protected function blockFencedCode($line) ); } - $block = array( + return array( 'char' => $line['text'][0], 'element' => array( 'name' => 'pre', @@ -548,7 +565,6 @@ protected function blockFencedCode($line) ), ); - return $block; } } @@ -565,7 +581,7 @@ protected function blockFencedCode($line) protected function blockFencedCodeContinue($line, $block) { if (isset($block['complete'])) { - return; + return null; } if (isset($block['interrupted'])) { @@ -587,23 +603,7 @@ protected function blockFencedCodeContinue($line, $block) return $block; } - /** - * Continue processing a fenced code block. - * - * This method appends additional lines to an existing fenced code block. - * - * @param array $line The line of text to process. - * @param array $block The current fenced code block being processed. - * @return array|null The updated fenced code block or null if not applicable. - */ - protected function blockFencedCodeComplete($block) - { - $text = $block['element']['text']['text']; - - $block['element']['text']['text'] = $text; - - return $block; - } + /** * Complete the fenced code block. @@ -623,20 +623,18 @@ protected function blockHeader($line) } if ($level > 6) { - return; + return null; } $text = trim($line['text'], '# '); - $block = array( + return array( 'element' => array( 'name' => 'h' . min(6, $level), 'text' => $text, 'handler' => 'line', ), ); - - return $block; } } @@ -693,7 +691,7 @@ protected function blockList($line) * @param array $block The current list block being processed. * @return array|null The updated list block or null if not applicable. */ - protected function blockListContinue($line, array $block) + protected function blockListContinue($line, array $block) //NOSONAR { if ($block['indent'] === $line['indent'] && preg_match('/^' . $block['pattern'] . '(?:[ ]+(.*)|$)/', $line['text'], $matches)) { if (isset($block['interrupted'])) { @@ -726,7 +724,7 @@ protected function blockListContinue($line, array $block) } if (!isset($block['interrupted'])) { - $text = preg_replace('/^[ ]{0,4}/', '', $line['body']); + $text = preg_replace('/^[ ]{0,4}/', '', $line['body']); //NOSONAR $block['li']['text'][] = $text; @@ -736,7 +734,7 @@ protected function blockListContinue($line, array $block) if ($line['indent'] > 0) { $block['li']['text'][] = ''; - $text = preg_replace('/^[ ]{0,4}/', '', $line['body']); + $text = preg_replace('/^[ ]{0,4}/', '', $line['body']); //NOSONAR $block['li']['text'][] = $text; @@ -777,17 +775,17 @@ protected function blockListComplete(array $block) */ protected function blockQuote($line) { - if (preg_match('/^>[ ]?(.*)/', $line['text'], $matches)) { - $block = array( + if (preg_match('/^>[ ]?(.*)/', $line['text'], $matches)) //NOSONAR + { + return array( 'element' => array( 'name' => 'blockquote', 'handler' => 'lines', 'text' => (array) $matches[1], ), ); - - return $block; } + return null; } /** @@ -801,7 +799,8 @@ protected function blockQuote($line) */ protected function blockQuoteContinue($line, array $block) { - if ($line['text'][0] === '>' && preg_match('/^>[ ]?(.*)/', $line['text'], $matches)) { + if ($line['text'][0] === '>' && preg_match('/^>[ ]?(.*)/', $line['text'], $matches)) //NOSONAR + { if (isset($block['interrupted'])) { $block['element']['text'][] = ''; @@ -831,13 +830,12 @@ protected function blockQuoteContinue($line, array $block) protected function blockRule($line) { if (preg_match('/^([' . $line['text'][0] . '])([ ]*\1){2,}[ ]*$/', $line['text'])) { - $block = array( + return array( 'element' => array( 'name' => 'hr' ), ); - return $block; } } @@ -853,7 +851,7 @@ protected function blockRule($line) protected function blockSetextHeader($line, array $block = null) { if (!isset($block) || isset($block['type']) || isset($block['interrupted'])) { - return; + return null; } if (chop($line['text'], $line['text'][0]) === '') { @@ -871,17 +869,17 @@ protected function blockSetextHeader($line, array $block = null) * @param array $line The line of text to process. * @return array|null The constructed markup block or null if not applicable. */ - protected function blockMarkup($line) + protected function blockMarkup($line) //NOSONAR { if ($this->markupEscaped || $this->safeMode) { - return; + return null; } if (preg_match('/^<(\w[\w-]*)(?:[ ]*' . $this->regexHtmlAttribute . ')*[ ]*(\/)?>/', $line['text'], $matches)) { $element = strtolower($matches[1]); if (in_array($element, $this->textLevelElements)) { - return; + return null; } $block = array( @@ -902,7 +900,7 @@ protected function blockMarkup($line) } } else { if (isset($matches[2]) || in_array($matches[1], $this->voidElements)) { - return; + return null; } if (preg_match('/<\/' . $matches[1] . '>[ ]*$/i', $remainder)) { @@ -925,7 +923,7 @@ protected function blockMarkup($line) protected function blockMarkupContinue($line, array $block) { if (isset($block['closed'])) { - return; + return null; } if (preg_match('/^<' . $block['name'] . '(?:[ ]*' . $this->regexHtmlAttribute . ')*[ ]*>/i', $line['text'])) # open @@ -963,7 +961,8 @@ protected function blockMarkupContinue($line, array $block) */ protected function blockReference($line) { - if (preg_match('/^\[(.+?)\]:[ ]*?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $line['text'], $matches)) { + if (preg_match('/^\[(.+?)\]:[ ]*?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $line['text'], $matches)) //NOSONAR + { $id = strtolower($matches[1]); $Data = array( @@ -977,11 +976,10 @@ protected function blockReference($line) $this->definitionData['Reference'][$id] = $Data; - $block = array( + return array( 'hidden' => true, ); - return $block; } } @@ -994,10 +992,10 @@ protected function blockReference($line) * @param array|null $block The current block being processed. * @return array|null The constructed table block or null if not applicable. */ - protected function blockTable($line, array $block = null) + protected function blockTable($line, array $block = null) //NOSONAR { if (!isset($block) || isset($block['type']) || isset($block['interrupted'])) { - return; + return null; } if (strpos($block['element']['text'], '|') !== false && chop($line['text'], ' -:|') === '') { @@ -1102,10 +1100,10 @@ protected function blockTable($line, array $block = null) * @param array $block The current table block being processed. * @return array|null The updated table block or null if not applicable. */ - protected function blockTableContinue($line, array $block) + protected function blockTableContinue($line, $block) //NOSONAR { if (isset($block['interrupted'])) { - return; + return null; } if ($line['text'][0] === '|' || strpos($line['text'], '|')) { @@ -1158,7 +1156,7 @@ protected function blockTableContinue($line, array $block) */ protected function paragraph($line) { - $block = array( + return array( 'element' => array( 'name' => 'p', 'text' => $line['text'], @@ -1166,7 +1164,6 @@ protected function paragraph($line) ), ); - return $block; } /** @@ -1178,7 +1175,7 @@ protected function paragraph($line) * @param array $nonNestables An array of non-nestable inline types. * @return string The processed markup with inline elements. */ - protected $InlineTypes = array( + protected $inlineTypes = array( '"' => array('SpecialCharacter'), '!' => array('Image'), '&' => array('SpecialCharacter'), @@ -1206,7 +1203,7 @@ protected function paragraph($line) * @param array $nonNestables An array of non-nestable inline types. * @return string The processed markup with inline elements. */ - public function line($text, $nonNestables = array()) + public function line($text, $nonNestables = array()) //NOSONAR { $markup = ''; @@ -1219,7 +1216,7 @@ public function line($text, $nonNestables = array()) $Excerpt = array('text' => $excerpt, 'context' => $text); - foreach ($this->InlineTypes[$marker] as $inlineType) { + foreach ($this->inlineTypes[$marker] as $inlineType) { # check to see if the current inline type is nestable in the current context if (!empty($nonNestables) && in_array($inlineType, $nonNestables)) { @@ -1293,7 +1290,7 @@ protected function inlineCode($Excerpt) if (preg_match('/^(' . $marker . '+)[ ]*(.+?)[ ]*(? strlen($matches[0]), @@ -1346,17 +1343,17 @@ protected function inlineEmailTag($Excerpt) protected function inlineEmphasis($Excerpt) { if (!isset($Excerpt['text'][1])) { - return; + return null; } $marker = $Excerpt['text'][0]; - if ($Excerpt['text'][1] === $marker && preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches)) { + if ($Excerpt['text'][1] === $marker && preg_match($this->strongRegex[$marker], $Excerpt['text'], $matches)) { $emphasis = 'strong'; - } elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches)) { + } elseif (preg_match($this->emRegex[$marker], $Excerpt['text'], $matches)) { $emphasis = 'em'; } else { - return; + return null; } return array( @@ -1398,7 +1395,7 @@ protected function inlineEscapeSequence($Excerpt) protected function inlineImage($Excerpt) { if (!isset($Excerpt['text'][1]) || $Excerpt['text'][1] !== '[') { - return; + return null; } $Excerpt['text'] = substr($Excerpt['text'], 1); @@ -1406,7 +1403,7 @@ protected function inlineImage($Excerpt) $Link = $this->inlineLink($Excerpt); if ($Link === null) { - return; + return null; } $Inline = array( @@ -1450,17 +1447,19 @@ protected function inlineLink($Excerpt) $remainder = $Excerpt['text']; - if (preg_match('/\[((?:[^][]++|(?R))*+)\]/', $remainder, $matches)) { + if (preg_match('/\[((?:[^][]++|(?R))*+)\]/', $remainder, $matches)) //NOSONAR + { $element['text'] = $matches[1]; $extent += strlen($matches[0]); $remainder = substr($remainder, $extent); } else { - return; + return null; } - if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*"|\'[^\']*\'))?\s*[)]/', $remainder, $matches)) { + if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*"|\'[^\']*\'))?\s*[)]/', $remainder, $matches)) //NOSONAR + { $element['attributes']['href'] = $matches[1]; if (isset($matches[2])) { @@ -1479,7 +1478,7 @@ protected function inlineLink($Excerpt) } if (!isset($this->definitionData['Reference'][$definition])) { - return; + return null; } $Definition = $this->definitionData['Reference'][$definition]; @@ -1500,20 +1499,22 @@ protected function inlineLink($Excerpt) * @param array $Excerpt Contains the text to be processed. * @return array|null Returns an array with the markup and its extent, or null if no valid markup is found. */ - protected function inlineMarkup($Excerpt) + protected function inlineMarkup($Excerpt) //NOSONAR { if ($this->markupEscaped || $this->safeMode || strpos($Excerpt['text'], '>') === false) { - return; + return null; } - if ($Excerpt['text'][1] === '/' && preg_match('/^<\/\w[\w-]*[ ]*>/s', $Excerpt['text'], $matches)) { + if ($Excerpt['text'][1] === '/' && preg_match('/^<\/\w[\w-]*[ ]*>/s', $Excerpt['text'], $matches)) //NOSONAR + { return array( 'markup' => $matches[0], 'extent' => strlen($matches[0]), ); } - if ($Excerpt['text'][1] === '!' && preg_match('/^/s', $Excerpt['text'], $matches)) { + if ($Excerpt['text'][1] === '!' && preg_match('/^/s', $Excerpt['text'], $matches)) //NOSONAR + { return array( 'markup' => $matches[0], 'extent' => strlen($matches[0]), @@ -1562,7 +1563,7 @@ protected function inlineSpecialCharacter($Excerpt) protected function inlineStrikethrough($Excerpt) { if (!isset($Excerpt['text'][1])) { - return; + return null; } if ($Excerpt['text'][1] === '~' && preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches)) { @@ -1586,13 +1587,14 @@ protected function inlineStrikethrough($Excerpt) protected function inlineUrl($Excerpt) { if ($this->urlsLinked !== true || !isset($Excerpt['text'][2]) || $Excerpt['text'][2] !== '/') { - return; + return null; } - if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE)) { + if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE)) //NOSONAR + { $url = $matches[0][0]; - $Inline = array( + return array( 'extent' => strlen($matches[0][0]), 'position' => $matches[0][1], 'element' => array( @@ -1604,7 +1606,6 @@ protected function inlineUrl($Excerpt) ), ); - return $Inline; } } @@ -1641,9 +1642,9 @@ protected function inlineUrlTag($Excerpt) protected function unmarkedText($text) { if ($this->breaksEnabled) { - $text = preg_replace('/[ ]*\n/', "
\n", $text); + $text = preg_replace('/[ ]*\n/', "
\n", $text); //NOSONAR } else { - $text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "
\n", $text); + $text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "
\n", $text); //NOSONAR $text = str_replace(" \n", "\n", $text); } @@ -1656,7 +1657,7 @@ protected function unmarkedText($text) * @param array $element The element to be rendered as HTML. * @return string The generated HTML markup for the element. */ - protected function element(array $element) + protected function element(array $element) //NOSONAR { if ($this->safeMode) { $element = $this->sanitiseElement($element); @@ -1761,9 +1762,7 @@ protected function li($lines) */ public function parse($text) { - $markup = $this->text($text); - - return $markup; + return $this->text($text); } /** @@ -1786,12 +1785,8 @@ protected function sanitiseElement(array $element) if (!empty($element['attributes'])) { foreach ($element['attributes'] as $att => $val) { - # filter out badly parsed attribute - if (!preg_match($goodAttribute, $att)) { - unset($element['attributes'][$att]); - } - # dump onevent attribute - elseif (self::striAtStart($att, 'on')) { + # filter out badly parsed attribute or dump onevent attribute + if (!preg_match($goodAttribute, $att) || self::striAtStart($att, 'on')) { unset($element['attributes'][$att]); } } @@ -1889,7 +1884,7 @@ public static function instance($name = 'default') /** * @var array Regular expressions for strong emphasis syntax. */ - protected $StrongRegex = array( + protected $strongRegex = array( '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s', '_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us', ); @@ -1897,7 +1892,7 @@ public static function instance($name = 'default') /** * @var array Regular expressions for emphasis syntax. */ - protected $EmRegex = array( + protected $emRegex = array( '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s', '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us', ); From b10f4d3243d451f877fd4710b51367b44c1354a8 Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Fri, 8 Nov 2024 09:45:15 +0700 Subject: [PATCH 08/12] Update PicoLocale.php --- src/Util/PicoLocale.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Util/PicoLocale.php b/src/Util/PicoLocale.php index 1ff9778..1186da5 100644 --- a/src/Util/PicoLocale.php +++ b/src/Util/PicoLocale.php @@ -5,6 +5,11 @@ /** * Locale * + * The `PicoLocale` class serves as a container for a collection of predefined locale constants. + * These constants represent different locale identifiers for various countries, regions, and languages, using the format of language-region (e.g., "en_US" for English in the United States, "fr_FR" for French in France). + * + * Each constant corresponds to a specific locale, which can be used in applications to handle internationalization (i18n), localization (l10n), and formatting of data such as dates, currencies, and numbers, based on the user's regional settings. + * * @author Kamshory * @package MagicObject\Util * @link https://github.com/Planetbiru/MagicObject From d62e61c279cb1c6800ac72cd13d2ff68bb592de9 Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Fri, 8 Nov 2024 10:16:07 +0700 Subject: [PATCH 09/12] Update Txt.php --- src/Txt.php | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/src/Txt.php b/src/Txt.php index 41ea42f..7a25759 100644 --- a/src/Txt.php +++ b/src/Txt.php @@ -5,25 +5,51 @@ /** * Class Txt * - * A utility class that provides dynamic handling of static method calls. - * This class allows for flexible interaction by returning the names of - * methods that are called statically but are not explicitly defined within - * the class. It can be useful for implementing dynamic behavior or - * creating a fluent interface. + * A utility class that provides dynamic handling of static method calls and dynamic property access. + * This class allows for flexible interaction by returning the names of methods and properties + * that are called statically or accessed dynamically but are not explicitly defined within the class. + * It can be useful for implementing dynamic behavior or creating a fluent interface. */ class Txt { /** * Handles static calls to undefined methods. * - * This method returns the name of the method being called. + * This method intercepts calls to static methods that are not explicitly defined in the class + * and returns the name of the method being called. * * @param string $name The name of the method being called. * @param array $arguments An array of arguments passed to the method. - * @return string|null The name of the called method, or null if no arguments are provided. + * @return string The name of the called method. */ public static function __callStatic($name, $arguments) { return $name; } + + /** + * Returns a new instance of the Txt class. + * + * This method allows you to retrieve an instance of the Txt class to perform non-static operations, + * such as dynamic property access using the __get() magic method. + * + * @return self A new instance of the Txt class. + */ + public function getInstance() + { + return new self; + } + + /** + * Handles dynamic access to undefined properties. + * + * This method is invoked when an undefined property is accessed on an instance of the Txt class. + * It returns the name of the property being accessed. + * + * @param string $name The name of the property being accessed. + * @return string The name of the accessed property. + */ + public function __get($name) { + return $name; + } } From 4dacd7beeea3fa534bd5f19ea2ecedfe147049c7 Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Fri, 8 Nov 2024 10:17:49 +0700 Subject: [PATCH 10/12] Update Txt.php --- src/Txt.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Txt.php b/src/Txt.php index 7a25759..7c17c47 100644 --- a/src/Txt.php +++ b/src/Txt.php @@ -35,7 +35,20 @@ public static function __callStatic($name, $arguments) * * @return self A new instance of the Txt class. */ - public function getInstance() + public static function getInstance() + { + return new self; + } + + /** + * Returns a new instance of the Txt class. + * + * This method allows you to retrieve an instance of the Txt class to perform non-static operations, + * such as dynamic property access using the __get() magic method. + * + * @return self A new instance of the Txt class. + */ + public static function of() { return new self; } From 345b71c8246d251b4194e0c17d318bc114fb2eca Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Fri, 8 Nov 2024 10:18:43 +0700 Subject: [PATCH 11/12] Update Txt.php --- src/Txt.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Txt.php b/src/Txt.php index 7c17c47..3d3af09 100644 --- a/src/Txt.php +++ b/src/Txt.php @@ -22,7 +22,7 @@ class Txt * @param array $arguments An array of arguments passed to the method. * @return string The name of the called method. */ - public static function __callStatic($name, $arguments) + public static function __callStatic($name, $arguments) //NOSONAR { return $name; } From 0ce14141f0fa9c34251ca5d36e60d3c88dd036ce Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Fri, 8 Nov 2024 10:22:56 +0700 Subject: [PATCH 12/12] Update Txt.php --- src/Txt.php | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/Txt.php b/src/Txt.php index 3d3af09..d315626 100644 --- a/src/Txt.php +++ b/src/Txt.php @@ -16,7 +16,8 @@ class Txt * Handles static calls to undefined methods. * * This method intercepts calls to static methods that are not explicitly defined in the class - * and returns the name of the method being called. + * and returns the name of the method being called. It allows for flexible handling of undefined + * static methods. * * @param string $name The name of the method being called. * @param array $arguments An array of arguments passed to the method. @@ -30,10 +31,10 @@ public static function __callStatic($name, $arguments) //NOSONAR /** * Returns a new instance of the Txt class. * - * This method allows you to retrieve an instance of the Txt class to perform non-static operations, - * such as dynamic property access using the __get() magic method. + * This method allows you to retrieve an instance of the Txt class for non-static operations. + * This instance can be used to access dynamic properties via the __get() magic method. * - * @return self A new instance of the Txt class. + * @return Txt A new instance of the Txt class. */ public static function getInstance() { @@ -41,12 +42,12 @@ public static function getInstance() } /** - * Returns a new instance of the Txt class. + * Creates and returns a new instance of the Txt class. * - * This method allows you to retrieve an instance of the Txt class to perform non-static operations, - * such as dynamic property access using the __get() magic method. + * Similar to getInstance(), this method allows you to retrieve an instance of the Txt class + * for non-static operations, such as dynamic property access using the __get() magic method. * - * @return self A new instance of the Txt class. + * @return Txt A new instance of the Txt class. */ public static function of() { @@ -62,7 +63,8 @@ public static function of() * @param string $name The name of the property being accessed. * @return string The name of the accessed property. */ - public function __get($name) { + public function __get($name) + { return $name; } }