Repository: opcodesio/log-viewer (language: php)

Issue:
No log files exist if the project path contains square brackets
Hello,

The wrong behavior:
---------------------
When opening the log-viewer no log files exist.

The cause:
-----------
Using PHP glob() function to get the log files breaks the package if the project path contains **square brackets**. This is a known issue with glob() function (https://bugs.php.net/bug.php?id=33047).

Example path:
--------------
`D:\laravel\[2]New\`

Glob() is used here:
--------------------
https://github.com/opcodesio/log-viewer/blob/ba4a4936715e2251d7316a8e4800ed9e1ee76017/src/LogViewerService.php#L39

If you can  use another approach to get the log files, it will be great!

Specs:
------
PHP: 8.1.6
Laravel: 8.83.23
OS: Windows 10



Discussion / hints:
Hey! Thanks for the report!

Because it's a Window's specific issue/bug, and it only happens to very specific use cases (the chances of someone using brackets in folder names is very low), I consider this to be a low priority.

Also, I just don't know a way of fixing this without introducing breaking changes (such as switching to Symfony's Finder component).

If anyone has a fix for this, please contribute! :)

Thanks 🙏 
Hi @arukompas, thank you for the response.

At least you can check the path, and if it has the breaking symbols, show a user message telling the user that there is a problem with the path with the square bracket, maybe he is able to change it, but don't let him in the dark :).
Good thought. If the squares can be seen in the value of `LogViewer::basePathForLogs()` then we can issue a warning/error. Otherwise, when adding additional glob patters to match directories **outside** of your laravel project - we won't be able to tell what we missed because of the square brackets.

I'll have a look at it this Friday :) Good suggestion!

Fix patch:
diff --git a/composer.json b/composer.json
index 42681e74..52ce092b 100644
--- a/composer.json
+++ b/composer.json
@@ -1,6 +1,6 @@
 {
     "name": "opcodesio/log-viewer",
-    "version": "v1.5.0",
+    "version": "v1.5.1",
     "description": "Fast and easy-to-use log viewer for your Laravel application",
     "keywords": [
         "arukompas",
diff --git a/src/LogViewerService.php b/src/LogViewerService.php
index c92b4bf4..f2f0bcd7 100755
--- a/src/LogViewerService.php
+++ b/src/LogViewerService.php
@@ -20,7 +20,23 @@ class LogViewerService
 
     protected function getFilePaths(): array
     {
-        $baseDir = $this->basePathForLogs();
+        // Because we'll use the base path as a parameter for `glob`, we should escape any
+        // glob's special characters and treat those as actual characters of the path.
+        // We can assume this, because it's the actual path of the Laravel app, not a user-defined
+        // search pattern.
+        if (PHP_OS_FAMILY === 'Windows') {
+            $baseDir = preg_replace_callback(
+                '/\[(.*)\]/i',
+                fn (array $matches) => '[[]'.$matches[1].'[]]',
+                $this->basePathForLogs()
+            );
+        } else {
+            $baseDir = str_replace(
+                ['*', '?', '\\', '[', ']'],
+                ['\*', '\?', '\\\\', '\[', '\]'],
+                $this->basePathForLogs()
+            );
+        }
         $files = [];
 
         foreach (config('log-viewer.include_files', []) as $pattern) {
@@ -28,7 +44,7 @@ protected function getFilePaths(): array
                 $pattern = $baseDir.$pattern;
             }
 
-            $files = array_merge($files, glob($pattern));
+            $files = array_merge($files, $this->getFilePathsMatchingPattern($pattern));
         }
 
         foreach (config('log-viewer.exclude_files', []) as $pattern) {
@@ -36,7 +52,7 @@ protected function getFilePaths(): array
                 $pattern = $baseDir.$pattern;
             }
 
-            $files = array_diff($files, glob($pattern));
+            $files = array_diff($files, $this->getFilePathsMatchingPattern($pattern));
         }
 
         $files = array_map('realpath', $files);
@@ -46,6 +62,11 @@ protected function getFilePaths(): array
         return array_values(array_reverse($files));
     }
 
+    protected function getFilePathsMatchingPattern($pattern)
+    {
+        return glob($pattern, GLOB_BRACE);
+    }
+
     public function basePathForLogs(): string
     {
         return Str::finish(realpath(storage_path('logs')), DIRECTORY_SEPARATOR);


Test patch:
diff --git a/tests/Unit/FilePathsTest.php b/tests/Unit/FilePathsTest.php
new file mode 100644
index 00000000..cc862b19
--- /dev/null
+++ b/tests/Unit/FilePathsTest.php
@@ -0,0 +1,37 @@
+<?php
+
+use Opcodes\LogViewer\Facades\LogViewer;
+use Opcodes\LogViewer\LogViewerService;
+
+test('handles square brackets in the logs path', function () {
+    // Get the original path inside which we'll create a dummy folder with square brackets
+    $originalBasePath = LogViewer::basePathForLogs();
+    $pathWithSquareBrackets = $originalBasePath.'[logs]'.DIRECTORY_SEPARATOR;
+    if (! file_exists($pathWithSquareBrackets)) {
+        mkdir($pathWithSquareBrackets, recursive: true);
+    }
+
+    // Let's mock LogViewer to return the new path as the base path for logs
+    app()->instance(
+        LogViewerService::class,
+        Mockery::mock(LogViewerService::class.'[basePathForLogs]')
+            ->shouldReceive('basePathForLogs')->andReturn($pathWithSquareBrackets)->getMock()
+    );
+    LogViewer::clearResolvedInstance('log-viewer');
+
+    // Create a dummy log file and make sure it's actually there
+    $expectedLogFilePath = $pathWithSquareBrackets.($fileName = 'laravel.log');
+    touch($expectedLogFilePath);
+    expect(file_exists($expectedLogFilePath))->toBeTrue();
+
+    // Act! Let's get the files and make sure they have found the log file created previously.
+    $logFiles = LogViewer::getFiles();
+
+    expect($logFiles)->toHaveCount(1)
+        ->and($logFiles[0]->name)->toBe($fileName)
+        ->and($logFiles[0]->path)->toBe($expectedLogFilePath);
+
+    // clean up!
+    unlink($expectedLogFilePath);
+    rmdir($pathWithSquareBrackets);
+});