Completed
Push — master ( b70c49...e834bf )
by Alessandro
13s
created

LogPrinter::getStackTrace()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 0
cts 0
cp 0
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
crap 2
1
<?php
2
declare(strict_types=1);
3
4
namespace Paraunit\Parser\JSON;
5
6
use Paraunit\Configuration\EnvVariables;
7
use PHPUnit\Framework\AssertionFailedError;
8
use PHPUnit\Framework\Test;
9
use PHPUnit\Framework\TestCase;
10
use PHPUnit\Framework\TestFailure;
11
use PHPUnit\Framework\TestListener;
12
use PHPUnit\Framework\TestSuite;
13
use PHPUnit\Framework\Warning;
14
use PHPUnit\Util;
15
16
/**
17
 * This class comes from Util\Log_JSON.
18
 * It's copied and refactored here because it's deprecated in PHPUnit 5.7 and it will be dropped in PHPUnit 6
19
 *
20
 * Class LogPrinter
21
 * @package Paraunit\Parser\JSON
22
 */
23
class LogPrinter extends Util\Printer implements TestListener
24
{
25
    const STATUS_ERROR = 'error';
26
    const STATUS_WARNING = 'warning';
27
    const STATUS_FAIL = 'fail';
28
    const STATUS_PASS = 'pass';
29
30
    const MESSAGE_INCOMPLETE_TEST = 'Incomplete Test: ';
31
    const MESSAGE_RISKY_TEST = 'Risky Test: ';
32
    const MESSAGE_SKIPPED_TEST = 'Skipped Test: ';
33
34
    /** @var resource */
35
    private $logFile;
36
37
    /** @var string */
38
    private $currentTestSuiteName;
39
40
    /** @var string */
41
    private $currentTestName;
42
43
    /** @var bool */
44
    private $currentTestPass;
45
46 9
    public function __construct()
47
    {
48 9
        $this->logFile = fopen($this->getLogFilename(), 'wt');
49 9
        $this->autoFlush = true;
50
    }
51
52
    /**
53
     * An error occurred.
54
     *
55
     * @param Test $test
56
     * @param \Exception $exception
57
     * @param float $time
58
     */
59 1 View Code Duplication
    public function addError(Test $test, \Exception $exception, $time)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
60
    {
61 1
        $this->writeCase(
62 1
            self::STATUS_ERROR,
63 1
            $time,
64 1
            $this->getStackTrace($exception),
65 1
            TestFailure::exceptionToString($exception),
66 1
            $test
67
        );
68
69 1
        $this->currentTestPass = false;
70
    }
71
72
    /**
73
     * A warning occurred.
74
     *
75
     * @param Test $test
76
     * @param Warning $warning
77
     * @param float $time
78
     */
79 1 View Code Duplication
    public function addWarning(Test $test, Warning $warning, $time)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
80
    {
81 1
        $this->writeCase(
82 1
            self::STATUS_WARNING,
83 1
            $time,
84 1
            $this->getStackTrace($warning),
85 1
            TestFailure::exceptionToString($warning),
86 1
            $test
87
        );
88
89 1
        $this->currentTestPass = false;
90
    }
91
92
    /**
93
     * A failure occurred.
94
     *
95
     * @param Test $test
96
     * @param AssertionFailedError $error
97
     * @param float $time
98
     */
99 1 View Code Duplication
    public function addFailure(Test $test, AssertionFailedError $error, $time)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
100
    {
101 1
        $this->writeCase(
102 1
            self::STATUS_FAIL,
103 1
            $time,
104 1
            $this->getStackTrace($error),
105 1
            TestFailure::exceptionToString($error),
106 1
            $test
107
        );
108
109 1
        $this->currentTestPass = false;
110
    }
111
112
    /**
113
     * Incomplete test.
114
     *
115
     * @param Test $test
116
     * @param \Exception $error
117
     * @param float $time
118
     */
119 1 View Code Duplication
    public function addIncompleteTest(Test $test, \Exception $error, $time)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
120
    {
121 1
        $this->writeCase(
122 1
            self::STATUS_ERROR,
123 1
            $time,
124 1
            $this->getStackTrace($error),
125 1
            self::MESSAGE_INCOMPLETE_TEST . $error->getMessage(),
126 1
            $test
127
        );
128
129 1
        $this->currentTestPass = false;
130
    }
131
132
    /**
133
     * Risky test.
134
     *
135
     * @param Test $test
136
     * @param \Exception $exception
137
     * @param float $time
138
     */
139 1 View Code Duplication
    public function addRiskyTest(Test $test, \Exception $exception, $time)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
140
    {
141 1
        $this->writeCase(
142 1
            self::STATUS_ERROR,
143 1
            $time,
144 1
            $this->getStackTrace($exception),
145 1
            self::MESSAGE_RISKY_TEST . $exception->getMessage(),
146 1
            $test
147
        );
148
149 1
        $this->currentTestPass = false;
150
    }
151
152
    /**
153
     * Skipped test.
154
     *
155
     * @param Test $test
156
     * @param \Exception $exception
157
     * @param float $time
158
     */
159 1 View Code Duplication
    public function addSkippedTest(Test $test, \Exception $exception, $time)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
160
    {
161 1
        $this->writeCase(
162 1
            self::STATUS_ERROR,
163 1
            $time,
164 1
            $this->getStackTrace($exception),
165 1
            self::MESSAGE_SKIPPED_TEST . $exception->getMessage(),
166 1
            $test
167
        );
168
169 1
        $this->currentTestPass = false;
170
    }
171
172
    /**
173
     * A testsuite started.
174
     *
175
     * @param TestSuite $suite
176
     * @throws \RuntimeException
177
     */
178 9
    public function startTestSuite(TestSuite $suite)
179
    {
180 9
        $this->currentTestSuiteName = $suite->getName();
181 9
        $this->currentTestName = '';
182
183 9
        $this->writeArray([
184 9
            'event' => 'suiteStart',
185 9
            'suite' => $this->currentTestSuiteName,
186 9
            'tests' => count($suite)
187
        ]);
188
    }
189
190 1
    public function endTestSuite(TestSuite $suite)
191
    {
192 1
        $this->currentTestSuiteName = '';
193 1
        $this->currentTestName = '';
194
    }
195
196 7
    public function startTest(Test $test)
197
    {
198 7
        $this->currentTestName = Util\Test::describe($test);
199 7
        $this->currentTestPass = true;
200
201 7
        $this->writeArray([
202 7
            'event' => 'testStart',
203 7
            'suite' => $this->currentTestSuiteName,
204 7
            'test' => $this->currentTestName
205
        ]);
206
    }
207
208
    /**
209
     * A test ended.
210
     *
211
     * @param Test $test
212
     * @param float $time
213
     */
214 1
    public function endTest(Test $test, $time)
215
    {
216 1
        if ($this->currentTestPass) {
217 1
            $this->writeCase(self::STATUS_PASS, $time, [], '', $test);
218
        }
219
    }
220
221
    /**
222
     * @param string $status
223
     * @param float $time
224
     * @param array $trace
225
     * @param string $message
226
     * @param Test|TestCase|null $test
227
     */
228 7
    private function writeCase($status, $time, array $trace = [], $message = '', $test = null)
229
    {
230 7
        $output = '';
231
        // take care of TestSuite producing error (e.g. by running into exception) as TestSuite doesn't have hasOutput
232 7
        if ($test !== null && method_exists($test, 'hasOutput') && $test->hasOutput()) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface PHPUnit\Framework\Test as the method hasOutput() does only exist in the following implementations of said interface: AbstractTest, AssertionExampleTest, BankAccountTest, BankAccountWithCustomExtensionTest, BeforeAndAfterTest, BeforeClassAndAfterClassTest, BeforeClassWithOnlyDataProviderTest, ChangeCurrentWorkingDirectoryTest, ClonedDependencyTest, ConcreteTest, ConcreteWithMyCustomExtensionTest, CoverageClassExtendedTest, CoverageClassTest, CoverageFunctionParenthesesTest, CoverageFunctionParenthesesWhitespaceTest, CoverageFunctionTest, CoverageMethodOneLineAnnotationTest, CoverageMethodParenthesesTest, CoverageMethodParenthesesWhitespaceTest, CoverageMethodTest, CoverageNamespacedFunctionTest, CoverageNoneTest, CoverageNotPrivateTest, CoverageNotProtectedTest, CoverageNotPublicTest, CoverageNothingTest, CoveragePrivateTest, CoverageProtectedTest, CoveragePublicTest, DataProviderDebugTest, DataProviderDependencyTest, DataProviderFilterTest, DataProviderIncompleteTest, DataProviderSkippedTest, DataProviderTest, DataProviderTestDoxTest, DependencyFailureTest, DependencySuccessTest, DummyBarTest, DummyFooTest, EmptyTestCaseTest, ExceptionInAssertPostConditionsTest, ExceptionInAssertPreConditionsTest, ExceptionInSetUpTest, ExceptionInTearDownTest, ExceptionInTest, ExceptionStackTest, ExceptionTest, Failure, FailureTest, FatalTest, Foo_Bar_Issue684Test, IgnoreCodeCoverageClassTest, IncompleteTest, InheritanceA, InheritanceB, InheritedTestCase, IniTest, IsolationTest, Issue1021Test, Issue1149Test, Issue1216Test, Issue1265Test, Issue1330Test, Issue1335Test, Issue1337Test, Issue1348Test, Issue1351Test, Issue1374Test, Issue1437Test, Issue1468Test, Issue1471Test, Issue1472Test, Issue1570Test, Issue2137Test, Issue2145Test, Issue2158Test, Issue2366Test, Issue2380Test, Issue2382Test, Issue2435Test, Issue244Test, Issue2591_SeparateClassPreserveTest, Issue2591_SeparateFunctionNoPreserveTest, Issue2591_SeparateFunctionPreserveTest, Issue2731Test, Issue2811Test, Issue322Test, Issue433Test, Issue445Test, Issue498Test, Issue503Test, Issue523Test, Issue578Test, Issue581Test, Issue74Test, Issue765Test, Issue797Test, MultiDependencyTest, MultipleDataProviderTest, My\Space\ExceptionNamespaceTest, NamespaceCoverageClassExtendedTest, NamespaceCoverageClassTest, NamespaceCoverageCoversClassPublicTest, NamespaceCoverageCoversClassTest, NamespaceCoverageMethodTest, NamespaceCoverageNotPrivateTest, NamespaceCoverageNotProtectedTest, NamespaceCoverageNotPublicTest, NamespaceCoveragePrivateTest, NamespaceCoverageProtectedTest, NamespaceCoveragePublicTest, NoArgTestCaseTest, NoTestCases, NotExistingCoveredElementTest, NotPublicTestCase, NotVoidTestCase, NothingTest, OneTest, OneTestCase, OutputTestCase, OverrideTestCase, PHPUnit\Framework\AssertTest, PHPUnit\Framework\BaseTestListenerTest, PHPUnit\Framework\ConstraintTest, PHPUnit\Framework\Constraint\ArraySubsetTest, PHPUnit\Framework\Constraint\CountTest, PHPUnit\Framework\Constr...eptionMessageRegExpTest, PHPUnit\Framework\Constraint\ExceptionMessageTest, PHPUnit\Framework\Constraint\IsJsonTest, PHPUnit\Framework\Constr...rrorMessageProviderTest, PHPUnit\Framework\Constraint\JsonMatchesTest, PHPUnit\Framework\IncompleteTestCase, PHPUnit\Framework\SkippedTestCase, PHPUnit\Framework\SuiteTest, PHPUnit\Framework\TestCase, PHPUnit\Framework\TestCaseTest, PHPUnit\Framework\TestFailureTest, PHPUnit\Framework\TestImplementorTest, PHPUnit\Framework\TestListenerTest, PHPUnit\Framework\WarningTestCase, PHPUnit\Runner\PhptTestCaseTest, PHPUnit\Util\ConfigurationTest, PHPUnit\Util\GetoptTest, PHPUnit\Util\GlobalStateTest, PHPUnit\Util\PHP\AbstractPhpProcessTest, PHPUnit\Util\RegularExpressionTest, PHPUnit\Util\TestDox\NamePrettifierTest, PHPUnit\Util\TestTest, PHPUnit\Util\XmlTest, RequirementsClassBeforeClassHookTest, RequirementsTest, StackTest, StopsOnWarningTest, Success, TemplateMethodsTest, Test, TestAutoreferenced, TestDoxGroupTest, TestError, TestIncomplete, TestSkipped, TestWithTest, ThrowExceptionTestCase, ThrowNoExceptionTestCase, TwoTest, WasRun, vendor\project\StatusTest.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
233
            $output = $test->getActualOutput();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface PHPUnit\Framework\Test as the method getActualOutput() does only exist in the following implementations of said interface: AbstractTest, AssertionExampleTest, BankAccountTest, BankAccountWithCustomExtensionTest, BeforeAndAfterTest, BeforeClassAndAfterClassTest, BeforeClassWithOnlyDataProviderTest, ChangeCurrentWorkingDirectoryTest, ClonedDependencyTest, ConcreteTest, ConcreteWithMyCustomExtensionTest, CoverageClassExtendedTest, CoverageClassTest, CoverageFunctionParenthesesTest, CoverageFunctionParenthesesWhitespaceTest, CoverageFunctionTest, CoverageMethodOneLineAnnotationTest, CoverageMethodParenthesesTest, CoverageMethodParenthesesWhitespaceTest, CoverageMethodTest, CoverageNamespacedFunctionTest, CoverageNoneTest, CoverageNotPrivateTest, CoverageNotProtectedTest, CoverageNotPublicTest, CoverageNothingTest, CoveragePrivateTest, CoverageProtectedTest, CoveragePublicTest, DataProviderDebugTest, DataProviderDependencyTest, DataProviderFilterTest, DataProviderIncompleteTest, DataProviderSkippedTest, DataProviderTest, DataProviderTestDoxTest, DependencyFailureTest, DependencySuccessTest, DummyBarTest, DummyFooTest, EmptyTestCaseTest, ExceptionInAssertPostConditionsTest, ExceptionInAssertPreConditionsTest, ExceptionInSetUpTest, ExceptionInTearDownTest, ExceptionInTest, ExceptionStackTest, ExceptionTest, Failure, FailureTest, FatalTest, Foo_Bar_Issue684Test, IgnoreCodeCoverageClassTest, IncompleteTest, InheritanceA, InheritanceB, InheritedTestCase, IniTest, IsolationTest, Issue1021Test, Issue1149Test, Issue1216Test, Issue1265Test, Issue1330Test, Issue1335Test, Issue1337Test, Issue1348Test, Issue1351Test, Issue1374Test, Issue1437Test, Issue1468Test, Issue1471Test, Issue1472Test, Issue1570Test, Issue2137Test, Issue2145Test, Issue2158Test, Issue2366Test, Issue2380Test, Issue2382Test, Issue2435Test, Issue244Test, Issue2591_SeparateClassPreserveTest, Issue2591_SeparateFunctionNoPreserveTest, Issue2591_SeparateFunctionPreserveTest, Issue2731Test, Issue2811Test, Issue322Test, Issue433Test, Issue445Test, Issue498Test, Issue503Test, Issue523Test, Issue578Test, Issue581Test, Issue74Test, Issue765Test, Issue797Test, MultiDependencyTest, MultipleDataProviderTest, My\Space\ExceptionNamespaceTest, NamespaceCoverageClassExtendedTest, NamespaceCoverageClassTest, NamespaceCoverageCoversClassPublicTest, NamespaceCoverageCoversClassTest, NamespaceCoverageMethodTest, NamespaceCoverageNotPrivateTest, NamespaceCoverageNotProtectedTest, NamespaceCoverageNotPublicTest, NamespaceCoveragePrivateTest, NamespaceCoverageProtectedTest, NamespaceCoveragePublicTest, NoArgTestCaseTest, NoTestCases, NotExistingCoveredElementTest, NotPublicTestCase, NotVoidTestCase, NothingTest, OneTest, OneTestCase, OutputTestCase, OverrideTestCase, PHPUnit\Framework\AssertTest, PHPUnit\Framework\BaseTestListenerTest, PHPUnit\Framework\ConstraintTest, PHPUnit\Framework\Constraint\ArraySubsetTest, PHPUnit\Framework\Constraint\CountTest, PHPUnit\Framework\Constr...eptionMessageRegExpTest, PHPUnit\Framework\Constraint\ExceptionMessageTest, PHPUnit\Framework\Constraint\IsJsonTest, PHPUnit\Framework\Constr...rrorMessageProviderTest, PHPUnit\Framework\Constraint\JsonMatchesTest, PHPUnit\Framework\IncompleteTestCase, PHPUnit\Framework\SkippedTestCase, PHPUnit\Framework\SuiteTest, PHPUnit\Framework\TestCase, PHPUnit\Framework\TestCaseTest, PHPUnit\Framework\TestFailureTest, PHPUnit\Framework\TestImplementorTest, PHPUnit\Framework\TestListenerTest, PHPUnit\Framework\WarningTestCase, PHPUnit\Runner\PhptTestCaseTest, PHPUnit\Util\ConfigurationTest, PHPUnit\Util\GetoptTest, PHPUnit\Util\GlobalStateTest, PHPUnit\Util\PHP\AbstractPhpProcessTest, PHPUnit\Util\RegularExpressionTest, PHPUnit\Util\TestDox\NamePrettifierTest, PHPUnit\Util\TestTest, PHPUnit\Util\XmlTest, RequirementsClassBeforeClassHookTest, RequirementsTest, StackTest, StopsOnWarningTest, Success, TemplateMethodsTest, Test, TestAutoreferenced, TestDoxGroupTest, TestError, TestIncomplete, TestSkipped, TestWithTest, ThrowExceptionTestCase, ThrowNoExceptionTestCase, TwoTest, WasRun, vendor\project\StatusTest.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
234
        }
235 7
        $this->writeArray([
236 7
            'event' => 'test',
237 7
            'suite' => $this->currentTestSuiteName,
238 7
            'test' => $this->currentTestName,
239 7
            'status' => $status,
240 7
            'time' => $time,
241 7
            'trace' => $trace,
242 7
            'message' => $this->convertToUtf8($message),
243 7
            'output' => $output,
244
        ]);
245
    }
246
247
    /**
248
     * @param array $buffer
249
     */
250
    private function writeArray($buffer)
251
    {
252 9
        array_walk_recursive($buffer, function (&$input) {
253 9
            if (is_string($input)) {
254 9
                $input = $this->convertToUtf8($input);
255
            }
256 9
        });
257
258 9
        $this->writeToLog(json_encode($buffer, JSON_PRETTY_PRINT));
259
    }
260
261 9
    private function writeToLog($buffer)
262
    {
263
        // ignore everything that is not a JSON object
264 9
        if ($buffer != '' && $buffer[0] === '{') {
265 9
            \fwrite($this->logFile, $buffer);
266 9
            \fflush($this->logFile);
267
        }
268
    }
269
270
    /**
271
     * @return string
272
     * @throws \RuntimeException
273
     * @throws \InvalidArgumentException
274
     */
275 9
    private function getLogFilename(): string
276
    {
277 9
        $logDir = $this->getLogDirectory();
278 9 View Code Duplication
        if (! @mkdir($logDir, 0777, true) && ! is_dir($logDir)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
279
            throw new \RuntimeException('Cannot create folder for JSON logs');
280
        }
281
282 9
        $logFilename = getenv(EnvVariables::PROCESS_UNIQUE_ID);
283 9
        if ($logFilename === false) {
284
            throw new \InvalidArgumentException('Log filename not received: environment variable not set');
285
        }
286
287 9
        return $logDir . $logFilename . '.json.log';
288
    }
289
290
    /**
291
     * @return string
292
     * @throws \InvalidArgumentException
293
     */
294 9
    private function getLogDirectory(): string
295
    {
296 9
        $logDirectory = getenv(EnvVariables::LOG_DIR);
297
298 9
        if ($logDirectory === false) {
299
            throw new \InvalidArgumentException('Log directory not received: environment variable not set');
300
        }
301
302 9
        if (substr($logDirectory, -1) !== DIRECTORY_SEPARATOR) {
303
            $logDirectory .= DIRECTORY_SEPARATOR;
304
        }
305
306 9
        return $logDirectory;
307
    }
308
309 9
    private function convertToUtf8($string): string
310
    {
311 9
        if (! \mb_detect_encoding($string, 'UTF-8', true)) {
312
            return \mb_convert_encoding($string, 'UTF-8');
313
        }
314
315 9
        return $string;
316
    }
317
318
    private function getStackTrace($error): array
319
    {
320
        /** @var string[] $trace */
321
        $trace = Util\Filter::getFilteredStacktrace($error, false);
322
        
323
        return $trace;
324
    }
325
}
326