Initial import from aternosorg/codex-minecraft
This commit is contained in:
43
.github/workflows/tests.yaml
vendored
Normal file
43
.github/workflows/tests.yaml
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
name: Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tests:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
php-version: [ '8.4', '8.5' ]
|
||||||
|
|
||||||
|
name: Run tests on PHP v${{ matrix.php-version }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup PHP
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: ${{ matrix.php-version }}
|
||||||
|
|
||||||
|
- name: Set composer cache directory
|
||||||
|
id: composer-cache
|
||||||
|
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||||
|
|
||||||
|
- name: Restore composer from cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ${{ steps.composer-cache.outputs.dir }}
|
||||||
|
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||||
|
restore-keys: ${{ runner.os }}-composer-
|
||||||
|
|
||||||
|
- name: Install composer dependencies
|
||||||
|
run: composer install --no-interaction --prefer-dist --no-progress
|
||||||
|
|
||||||
|
- name: Run phpunit tests
|
||||||
|
run: vendor/bin/phpunit --testsuite tests --colors=always --testdox
|
||||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.idea
|
||||||
|
vendor
|
||||||
|
.phpunit.result.cache
|
||||||
19
LICENSE
Normal file
19
LICENSE
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2019-2026 Aternos GmbH
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
115
README.md
Normal file
115
README.md
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
# Codex
|
||||||
|
|
||||||
|
### About
|
||||||
|
|
||||||
|
Codex (*lat. roughly for "log"*) is a PHP library to read, parse, print and analyse log files to find problems and suggest possible
|
||||||
|
solutions. It was created mainly for Minecraft server logs but could be used for any other logs as well. This library provides a set
|
||||||
|
up for a structured log parsing implementation and provides some useful basic implementation, mainly based on RegEx. Every part
|
||||||
|
of this library can or even must be extended/overwritten while still following the interfaces, which ensure interoperability between
|
||||||
|
the different parts of this library.
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
```
|
||||||
|
composer require aternos/codex
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
This is a short introduction to the idea of Codex, for some more examples check the [test](test) folder
|
||||||
|
and/or read the [code](src).
|
||||||
|
|
||||||
|
### Logfile
|
||||||
|
|
||||||
|
A [`LogFile`](src/Log/File/LogFile.php) object implementing the [`LogFileInterface`](src/Log/File/LogFileInterface.php) object is required
|
||||||
|
to start reading a log. There are currently three different log file classes in this library.
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$logFile = new \Aternos\Codex\Log\File\StringLogFile("This is the log content");
|
||||||
|
$logFile = new \Aternos\Codex\Log\File\PathLogFile("/path/to/log");
|
||||||
|
$logFile = new \Aternos\Codex\Log\File\StreamLogFile(fopen("/path/to/log", "r"));
|
||||||
|
```
|
||||||
|
|
||||||
|
### Log
|
||||||
|
|
||||||
|
A [`Log`](src/Log/Log.php) object implementing the [`LogInterface`](src/Log/LogInterface.php) is the most important object
|
||||||
|
for the different operations. It represents the log content, which is split in [Entries](src/Log/EntryInterface.php) and [Lines](src/Log/LineInterface.php).
|
||||||
|
And it offers quick access to the detection, parsing and analysing functions and can define which classes are used
|
||||||
|
for those functions. If you know which log type you have or just want to test the default [Log](src/Log/Log.php) class, you can
|
||||||
|
directly create a new instance, otherwise you can use detection as described below.
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$log = new \Aternos\Codex\Log\Log();
|
||||||
|
$log->setLogFile($logFile);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Detection
|
||||||
|
|
||||||
|
If the log type (specifically the class name of the log type) is unknown you can use the [`Detective`](src/Detective/Detective.php) class
|
||||||
|
to automatically detect the log type. The `Detective` class gets a list of possible log class names and executes
|
||||||
|
their given [Detectors](src/Detective/DetectorInterface.php).
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$detective = new \Aternos\Codex\Detective\Detective();
|
||||||
|
$detective->addPossibleLogClass(\Aternos\Codex\Log\Log::class);
|
||||||
|
$log = $detective->detect();
|
||||||
|
```
|
||||||
|
|
||||||
|
The `detect()` function always returns a log object, if necessary it defaults to [`Log`](src/Log/Log.php).
|
||||||
|
|
||||||
|
### Parsing
|
||||||
|
|
||||||
|
Parsing reads the entire log and creates the [`Entry`](src/Log/EntryInterface.php) and [`Line`](src/Log/LineInterface.php) objects which
|
||||||
|
are parts of a [`Log`](src/Log/LogInterface.php) object. Different log types can use different parsers by overwriting the
|
||||||
|
`LogInterface::getDefaultParser()` function or by passing a parser object to the parse function.
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$log->parse();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Analysing
|
||||||
|
|
||||||
|
An analysis is performed by an [`Analyser`](src/Analyser/AnalyserInterface.php) on an [`AnalysableLog`](src/Log/AnalysableLogInterface.php) and returns
|
||||||
|
an [`Analysis`](src/Analysis/AnalysisInterface.php) object containing various [`Insight`](src/Analysis/InsightInterface.php) objects, e.g. a [`Problem`](src/Analysis/ProblemInterface.php)
|
||||||
|
or an [`Information`](src/Analysis/InformationInterface.php) object. Different log types can use different analysers by overwriting
|
||||||
|
the `AnalysableLogInterface::getDefaultAnalyser()` function or by passing an analyser object to the analyse function.
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$analysis = $log->analyse();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Printing
|
||||||
|
|
||||||
|
The entire [`Log`](src/Log/LogInterface.php) or just an [`Entry`](src/Log/EntryInterface.php) can be printed through a [`Printer`](src/Printer/PrinterInterface.php). The basic
|
||||||
|
[`DefaultPrinter`](src/Printer/DefaultPrinter.php) only prints the plain content line by line. The [`ModifiableDefaultPrinter`](src/Printer/ModifiableDefaultPrinter.php)
|
||||||
|
allows [`Modification`](src/Printer/ModificationInterface.php), e.g. to highlight certain characters/words.
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$printer = new \Aternos\Codex\Printer\DefaultPrinter();
|
||||||
|
$printer->setLog($log);
|
||||||
|
$printer->print();
|
||||||
|
|
||||||
|
$printer = new \Aternos\Codex\Printer\DefaultPrinter();
|
||||||
|
$printer->setEntry($entry);
|
||||||
|
$printer->print();
|
||||||
|
|
||||||
|
$printer = new \Aternos\Codex\Printer\ModifiableDefaultPrinter();
|
||||||
|
$printer->setLog($log);
|
||||||
|
$modification = new \Aternos\Codex\Printer\PatternModification();
|
||||||
|
$modification->setPattern('/foo/');
|
||||||
|
$modification->setReplacement('bar');
|
||||||
|
$printer->addModification($modification);
|
||||||
|
$printer->print();
|
||||||
|
```
|
||||||
32
composer.json
Normal file
32
composer.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "aternos/codex",
|
||||||
|
"description": "PHP library to read, parse, print and analyse log files.",
|
||||||
|
"license": "MIT",
|
||||||
|
"type": "library",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Matthias Neid",
|
||||||
|
"email": "matthias@aternos.org"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.4"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^12"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Aternos\\Codex\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Aternos\\Codex\\Test\\Src\\": "test/src/",
|
||||||
|
"Aternos\\Codex\\Test\\Tests\\": "test/tests/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "phpunit test/tests"
|
||||||
|
}
|
||||||
|
}
|
||||||
1695
composer.lock
generated
Normal file
1695
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
19
phpunit.xml
Normal file
19
phpunit.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit bootstrap="vendor/autoload.php"
|
||||||
|
colors="true"
|
||||||
|
displayDetailsOnTestsThatTriggerDeprecations="true"
|
||||||
|
displayDetailsOnTestsThatTriggerErrors="true"
|
||||||
|
displayDetailsOnTestsThatTriggerNotices="true"
|
||||||
|
displayDetailsOnTestsThatTriggerWarnings="true"
|
||||||
|
displayDetailsOnPhpunitDeprecations="true">
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="tests">
|
||||||
|
<directory>test/tests/</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
<source>
|
||||||
|
<include>
|
||||||
|
<directory>src</directory>
|
||||||
|
</include>
|
||||||
|
</source>
|
||||||
|
</phpunit>
|
||||||
27
src/Analyser/Analyser.php
Normal file
27
src/Analyser/Analyser.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Analyser;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\AnalysableLogInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Analyser
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Analyser
|
||||||
|
*/
|
||||||
|
abstract class Analyser implements AnalyserInterface
|
||||||
|
{
|
||||||
|
protected ?AnalysableLogInterface $log = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the log
|
||||||
|
*
|
||||||
|
* @param AnalysableLogInterface $log
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLog(AnalysableLogInterface $log): static
|
||||||
|
{
|
||||||
|
$this->log = $log;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/Analyser/AnalyserInterface.php
Normal file
29
src/Analyser/AnalyserInterface.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Analyser;
|
||||||
|
|
||||||
|
use Aternos\Codex\Analysis\AnalysisInterface;
|
||||||
|
use Aternos\Codex\Log\AnalysableLogInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface AnalyserInterface
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Analyser
|
||||||
|
*/
|
||||||
|
interface AnalyserInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Set the log
|
||||||
|
*
|
||||||
|
* @param AnalysableLogInterface $log
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLog(AnalysableLogInterface $log): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Analyse a log and return an Analysis
|
||||||
|
*
|
||||||
|
* @return AnalysisInterface
|
||||||
|
*/
|
||||||
|
public function analyse(): AnalysisInterface;
|
||||||
|
}
|
||||||
159
src/Analyser/PatternAnalyser.php
Normal file
159
src/Analyser/PatternAnalyser.php
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Analyser;
|
||||||
|
|
||||||
|
use Aternos\Codex\Analysis\Analysis;
|
||||||
|
use Aternos\Codex\Analysis\AnalysisInterface;
|
||||||
|
use Aternos\Codex\Analysis\PatternInsightInterface;
|
||||||
|
use Aternos\Codex\Log\EntryInterface;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class PatternAnalyser
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Analyser
|
||||||
|
*/
|
||||||
|
class PatternAnalyser extends Analyser
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var class-string<PatternInsightInterface>[]
|
||||||
|
*/
|
||||||
|
protected array $possibleInsightClasses = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set possible insight classes
|
||||||
|
*
|
||||||
|
* Every class must implement PatternInsightInterface
|
||||||
|
*
|
||||||
|
* @param class-string<PatternInsightInterface>[] $insightClasses
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setPossibleInsightClasses(array $insightClasses): static
|
||||||
|
{
|
||||||
|
$this->possibleInsightClasses = [];
|
||||||
|
foreach ($insightClasses as $insightClass) {
|
||||||
|
$this->addPossibleInsightClass($insightClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a possible insight class
|
||||||
|
*
|
||||||
|
* The class must implement PatternInsightInterface
|
||||||
|
*
|
||||||
|
* @param class-string<PatternInsightInterface> $insightClass
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function addPossibleInsightClass(string $insightClass): static
|
||||||
|
{
|
||||||
|
if (!is_subclass_of($insightClass, PatternInsightInterface::class)) {
|
||||||
|
throw new InvalidArgumentException("Class " . $insightClass . " does not implement " . PatternInsightInterface::class . ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->possibleInsightClasses[] = $insightClass;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a possible insight class
|
||||||
|
*
|
||||||
|
* @param class-string<PatternInsightInterface> $insightClass
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
protected function findPossibleInsightClass(string $insightClass): int
|
||||||
|
{
|
||||||
|
$index = array_search($insightClass, $this->possibleInsightClasses);
|
||||||
|
if ($index === false) {
|
||||||
|
throw new InvalidArgumentException("Class " . $insightClass . " not found in possible insight classes.");
|
||||||
|
}
|
||||||
|
return $index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a possible insight class
|
||||||
|
*
|
||||||
|
* @param class-string<PatternInsightInterface> $insightClass
|
||||||
|
*/
|
||||||
|
public function removePossibleInsightClass(string $insightClass): void
|
||||||
|
{
|
||||||
|
$index = $this->findPossibleInsightClass($insightClass);
|
||||||
|
unset($this->possibleInsightClasses[$index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override a possible insight class with a child class
|
||||||
|
*
|
||||||
|
* The $childInsightClass has to extend $parentInsightClass
|
||||||
|
*
|
||||||
|
* @param class-string<PatternInsightInterface> $parentInsightClass
|
||||||
|
* @param class-string<PatternInsightInterface> $childInsightClass
|
||||||
|
*/
|
||||||
|
public function overridePossibleInsightClass(string $parentInsightClass, string $childInsightClass): void
|
||||||
|
{
|
||||||
|
if (!is_subclass_of($childInsightClass, $parentInsightClass)) {
|
||||||
|
throw new InvalidArgumentException("Class " . $childInsightClass . " does not extend " . $parentInsightClass . ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
$index = $this->findPossibleInsightClass($parentInsightClass);
|
||||||
|
$this->possibleInsightClasses[$index] = $childInsightClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Analyse a log and return an Analysis
|
||||||
|
*
|
||||||
|
* @return AnalysisInterface
|
||||||
|
*/
|
||||||
|
public function analyse(): AnalysisInterface
|
||||||
|
{
|
||||||
|
$analysis = new Analysis();
|
||||||
|
$analysis->setLog($this->log);
|
||||||
|
|
||||||
|
foreach ($this->log as $entry) {
|
||||||
|
foreach ($this->possibleInsightClasses as $possibleInsightClass) {
|
||||||
|
/** @var PatternInsightInterface $possibleInsightClass */
|
||||||
|
$patterns = $possibleInsightClass::getPatterns();
|
||||||
|
foreach ($patterns as $patternKey => $pattern) {
|
||||||
|
$insights = $this->analyseEntry($entry, $possibleInsightClass, $patternKey, $pattern);
|
||||||
|
if ($insights) {
|
||||||
|
foreach ($insights as $insight) {
|
||||||
|
$analysis->addInsight($insight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $analysis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare the entry against the given pattern and create an insight object if it matches
|
||||||
|
*
|
||||||
|
* @param EntryInterface $entry
|
||||||
|
* @param string $possibleInsightClass
|
||||||
|
* @param mixed $patternKey
|
||||||
|
* @param string $pattern
|
||||||
|
* @return null|PatternInsightInterface[]
|
||||||
|
*/
|
||||||
|
protected function analyseEntry(EntryInterface $entry, string $possibleInsightClass, mixed $patternKey, string $pattern): ?array
|
||||||
|
{
|
||||||
|
$result = preg_match_all($pattern, $entry, $matches, PREG_SET_ORDER);
|
||||||
|
if ($result === false || $result === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$return = [];
|
||||||
|
foreach ($matches as $match) {
|
||||||
|
/** @var PatternInsightInterface $insight */
|
||||||
|
$insight = new $possibleInsightClass();
|
||||||
|
$insight->setMatches($match, $patternKey);
|
||||||
|
$insight->setEntry($entry);
|
||||||
|
|
||||||
|
$return[] = $insight;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
}
|
||||||
238
src/Analysis/Analysis.php
Normal file
238
src/Analysis/Analysis.php
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Analysis;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\LogInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Analysis
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Analysis
|
||||||
|
*/
|
||||||
|
class Analysis implements AnalysisInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var InsightInterface[]
|
||||||
|
*/
|
||||||
|
protected array $insights = [];
|
||||||
|
protected int $iterator = 0;
|
||||||
|
protected ?LogInterface $log = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set all insights at once in an array replacing the current insights
|
||||||
|
*
|
||||||
|
* @param InsightInterface[] $insights
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setInsights(array $insights = []): static
|
||||||
|
{
|
||||||
|
foreach ($insights as $insight) {
|
||||||
|
$insight->setAnalysis($this);
|
||||||
|
}
|
||||||
|
$this->insights = $insights;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an insight.
|
||||||
|
* If the insight already exists, we increase its counter.
|
||||||
|
*
|
||||||
|
* @param InsightInterface $insight
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function addInsight(InsightInterface $insight): static
|
||||||
|
{
|
||||||
|
$insight->setAnalysis($this);
|
||||||
|
|
||||||
|
foreach ($this as $existingInsight) {
|
||||||
|
if (get_class($insight) === get_class($existingInsight) && $existingInsight->isEqual($insight)) {
|
||||||
|
$existingInsight->increaseCounter();
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->insights[] = $insight;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all insights
|
||||||
|
*
|
||||||
|
* @return InsightInterface[]
|
||||||
|
*/
|
||||||
|
public function getInsights(): array
|
||||||
|
{
|
||||||
|
return $this->insights;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all insights that are extended from $extendedFrom (class name)
|
||||||
|
*
|
||||||
|
* @param class-string<InsightInterface> $extendedFrom
|
||||||
|
* @return InsightInterface[]
|
||||||
|
*/
|
||||||
|
public function getFilteredInsights(string $extendedFrom): array
|
||||||
|
{
|
||||||
|
$returnInsights = [];
|
||||||
|
foreach ($this->getInsights() as $insight) {
|
||||||
|
if ($insight instanceof $extendedFrom) {
|
||||||
|
$returnInsights[] = $insight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $returnInsights;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all problem insights
|
||||||
|
*
|
||||||
|
* @return ProblemInterface[]
|
||||||
|
*/
|
||||||
|
public function getProblems(): array
|
||||||
|
{
|
||||||
|
return $this->getFilteredInsights(ProblemInterface::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all information insights
|
||||||
|
*
|
||||||
|
* @return InformationInterface[]
|
||||||
|
*/
|
||||||
|
public function getInformation(): array
|
||||||
|
{
|
||||||
|
return $this->getFilteredInsights(InformationInterface::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current element
|
||||||
|
*
|
||||||
|
* @return InsightInterface
|
||||||
|
*/
|
||||||
|
public function current(): InsightInterface
|
||||||
|
{
|
||||||
|
return $this->insights[$this->iterator];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move forward to next element
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function next(): void
|
||||||
|
{
|
||||||
|
$this->iterator++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the key of the current element
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function key(): int
|
||||||
|
{
|
||||||
|
return $this->iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if current position is valid
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function valid(): bool
|
||||||
|
{
|
||||||
|
return array_key_exists($this->iterator, $this->insights);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rewind the Iterator to the first element
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function rewind(): void
|
||||||
|
{
|
||||||
|
$this->iterator = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count elements of an object
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function count(): int
|
||||||
|
{
|
||||||
|
return count($this->insights);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether an offset exists
|
||||||
|
*
|
||||||
|
* @param mixed $offset
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function offsetExists(mixed $offset): bool
|
||||||
|
{
|
||||||
|
return isset($this->insights[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset to retrieve
|
||||||
|
*
|
||||||
|
* @param mixed $offset
|
||||||
|
* @return InsightInterface
|
||||||
|
*/
|
||||||
|
public function offsetGet(mixed $offset): InsightInterface
|
||||||
|
{
|
||||||
|
return $this->insights[$offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset to set
|
||||||
|
*
|
||||||
|
* @param mixed $offset
|
||||||
|
* @param InsightInterface $value
|
||||||
|
*/
|
||||||
|
public function offsetSet(mixed $offset, mixed $value): void
|
||||||
|
{
|
||||||
|
$value->setAnalysis($this);
|
||||||
|
$this->insights[$offset] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset to unset
|
||||||
|
*
|
||||||
|
* @param mixed $offset
|
||||||
|
*/
|
||||||
|
public function offsetUnset(mixed $offset): void
|
||||||
|
{
|
||||||
|
unset($this->insights[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function jsonSerialize(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
"problems" => $this->getProblems(),
|
||||||
|
"information" => $this->getInformation()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param LogInterface $log
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLog(LogInterface $log): static
|
||||||
|
{
|
||||||
|
$this->log = $log;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return LogInterface|null
|
||||||
|
*/
|
||||||
|
public function getLog(): ?LogInterface
|
||||||
|
{
|
||||||
|
return $this->log;
|
||||||
|
}
|
||||||
|
}
|
||||||
77
src/Analysis/AnalysisInterface.php
Normal file
77
src/Analysis/AnalysisInterface.php
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Analysis;
|
||||||
|
|
||||||
|
use ArrayAccess;
|
||||||
|
use Aternos\Codex\Log\LogInterface;
|
||||||
|
use Countable;
|
||||||
|
use Iterator;
|
||||||
|
use JsonSerializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface AnalysisInterface
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Analysis
|
||||||
|
*/
|
||||||
|
interface AnalysisInterface extends Iterator, Countable, ArrayAccess, JsonSerializable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Set the log
|
||||||
|
*
|
||||||
|
* @param LogInterface $log
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLog(LogInterface $log): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the log
|
||||||
|
*
|
||||||
|
* @return LogInterface|null
|
||||||
|
*/
|
||||||
|
public function getLog(): ?LogInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set all insights at once in an array replacing the current insights
|
||||||
|
*
|
||||||
|
* @param InsightInterface[] $insights
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setInsights(array $insights = []): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an insight
|
||||||
|
*
|
||||||
|
* @param InsightInterface $insight
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function addInsight(InsightInterface $insight): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all insights
|
||||||
|
*
|
||||||
|
* @return InsightInterface[]
|
||||||
|
*/
|
||||||
|
public function getInsights(): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all problem insights
|
||||||
|
*
|
||||||
|
* @return ProblemInterface[]
|
||||||
|
*/
|
||||||
|
public function getProblems(): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all information insights
|
||||||
|
*
|
||||||
|
* @return InformationInterface[]
|
||||||
|
*/
|
||||||
|
public function getInformation(): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all insights that are extended from $extendedFrom (class name)
|
||||||
|
*
|
||||||
|
* @param class-string<InsightInterface> $extendedFrom
|
||||||
|
* @return InsightInterface[]
|
||||||
|
*/
|
||||||
|
public function getFilteredInsights(string $extendedFrom): array;
|
||||||
|
}
|
||||||
17
src/Analysis/AutomatableSolutionInterface.php
Normal file
17
src/Analysis/AutomatableSolutionInterface.php
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Analysis;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface AutomatableSolutionInterface
|
||||||
|
*
|
||||||
|
* This interface should be used to indicate
|
||||||
|
* that a solution can be solved automatically
|
||||||
|
* e.g. deletion/creation/modification of files
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Analysis
|
||||||
|
*/
|
||||||
|
interface AutomatableSolutionInterface extends SolutionInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
90
src/Analysis/Information.php
Normal file
90
src/Analysis/Information.php
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Analysis;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Information
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Analysis
|
||||||
|
*/
|
||||||
|
abstract class Information extends Insight implements InformationInterface
|
||||||
|
{
|
||||||
|
protected ?string $label = null;
|
||||||
|
protected mixed $value = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the information label
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getLabel(): string
|
||||||
|
{
|
||||||
|
return $this->label;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the information label
|
||||||
|
*
|
||||||
|
* @param string $label
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
protected function setLabel(string $label): static
|
||||||
|
{
|
||||||
|
$this->label = $label;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the information value
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getValue(): mixed
|
||||||
|
{
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the information value
|
||||||
|
*
|
||||||
|
* @param mixed $value
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setValue(mixed $value): static
|
||||||
|
{
|
||||||
|
$this->value = $value;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a human-readable message
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getMessage(): string
|
||||||
|
{
|
||||||
|
return $this->getLabel() . ": " . $this->getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the $insight object is equal with the current object
|
||||||
|
*
|
||||||
|
* @param InsightInterface $insight
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isEqual(InsightInterface $insight): bool
|
||||||
|
{
|
||||||
|
return $insight instanceof InformationInterface && $this->getLabel() === $insight->getLabel() && $this->getValue() === $insight->getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function jsonSerialize(): array
|
||||||
|
{
|
||||||
|
return array_merge(parent::jsonSerialize(), [
|
||||||
|
"label" => $this->getLabel(),
|
||||||
|
"value" => $this->getValue()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/Analysis/InformationInterface.php
Normal file
33
src/Analysis/InformationInterface.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Analysis;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface InformationInterface
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Analysis
|
||||||
|
*/
|
||||||
|
interface InformationInterface extends InsightInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the information label
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getLabel(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the information value
|
||||||
|
*
|
||||||
|
* @param mixed $value
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setValue(mixed $value): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the information value
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getValue(): mixed;
|
||||||
|
}
|
||||||
119
src/Analysis/Insight.php
Normal file
119
src/Analysis/Insight.php
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Analysis;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\EntryInterface;
|
||||||
|
use Aternos\Codex\Log\LogInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Insight
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Analysis
|
||||||
|
*/
|
||||||
|
abstract class Insight implements InsightInterface
|
||||||
|
{
|
||||||
|
protected ?AnalysisInterface $analysis = null;
|
||||||
|
protected ?EntryInterface $entry = null;
|
||||||
|
protected int $counter = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the related entry
|
||||||
|
*
|
||||||
|
* @param EntryInterface $entry
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setEntry(EntryInterface $entry): static
|
||||||
|
{
|
||||||
|
$this->entry = $entry;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the related entry
|
||||||
|
*
|
||||||
|
* @return EntryInterface
|
||||||
|
*/
|
||||||
|
public function getEntry(): EntryInterface
|
||||||
|
{
|
||||||
|
return $this->entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increase the counter for this insight
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function increaseCounter(): static
|
||||||
|
{
|
||||||
|
$this->counter++;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current counter value
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getCounterValue(): int
|
||||||
|
{
|
||||||
|
return $this->counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return $this->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function jsonSerialize(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'message' => $this->getMessage(),
|
||||||
|
'counter' => $this->getCounterValue(),
|
||||||
|
'entry' => $this->getEntry()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the related analysis
|
||||||
|
*
|
||||||
|
* @param AnalysisInterface $analysis
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setAnalysis(AnalysisInterface $analysis): static
|
||||||
|
{
|
||||||
|
$this->analysis = $analysis;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the related analysis
|
||||||
|
*
|
||||||
|
* @return AnalysisInterface|null
|
||||||
|
*/
|
||||||
|
public function getAnalysis(): ?AnalysisInterface
|
||||||
|
{
|
||||||
|
return $this->analysis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return LogInterface|null
|
||||||
|
*/
|
||||||
|
protected function getLog(): ?LogInterface
|
||||||
|
{
|
||||||
|
return $this->getAnalysis()?->getLog();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
protected function getLogContent(): ?string
|
||||||
|
{
|
||||||
|
return $this->getLog()?->getLogFile()?->getContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
78
src/Analysis/InsightInterface.php
Normal file
78
src/Analysis/InsightInterface.php
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Analysis;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\EntryInterface;
|
||||||
|
use JsonSerializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface InsightInterface
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Analysis
|
||||||
|
*/
|
||||||
|
interface InsightInterface extends JsonSerializable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get a human-readable message
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getMessage(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the related entry
|
||||||
|
*
|
||||||
|
* @param EntryInterface $entry
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setEntry(EntryInterface $entry): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the related entry
|
||||||
|
*
|
||||||
|
* @return EntryInterface
|
||||||
|
*/
|
||||||
|
public function getEntry(): EntryInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the $insight object is equal with the current object
|
||||||
|
*
|
||||||
|
* @param InsightInterface $insight
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isEqual(InsightInterface $insight): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increase the counter for this insight
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function increaseCounter(): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current counter value
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getCounterValue(): int;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the related analysis
|
||||||
|
*
|
||||||
|
* @param AnalysisInterface $analysis
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setAnalysis(AnalysisInterface $analysis): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the related analysis
|
||||||
|
*
|
||||||
|
* @return AnalysisInterface|null
|
||||||
|
*/
|
||||||
|
public function getAnalysis(): ?AnalysisInterface;
|
||||||
|
}
|
||||||
29
src/Analysis/PatternInsightInterface.php
Normal file
29
src/Analysis/PatternInsightInterface.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Analysis;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface PatternInsightInterface
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Analysis
|
||||||
|
*/
|
||||||
|
interface PatternInsightInterface extends InsightInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get an array of possible patterns
|
||||||
|
*
|
||||||
|
* The array key of the pattern will be passed to setMatches()
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public static function getPatterns(): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the matches from the pattern
|
||||||
|
*
|
||||||
|
* @param array $matches
|
||||||
|
* @param mixed $patternKey
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setMatches(array $matches, mixed $patternKey): void;
|
||||||
|
}
|
||||||
168
src/Analysis/Problem.php
Normal file
168
src/Analysis/Problem.php
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Analysis;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Problem
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Analysis
|
||||||
|
*/
|
||||||
|
abstract class Problem extends Insight implements ProblemInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var SolutionInterface[]
|
||||||
|
*/
|
||||||
|
protected array $solutions = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected int $iterator = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set all solutions at once in an array replacing the current solutions
|
||||||
|
*
|
||||||
|
* @param SolutionInterface[] $solutions
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setSolutions(array $solutions = []): static
|
||||||
|
{
|
||||||
|
$this->solutions = $solutions;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a solution
|
||||||
|
*
|
||||||
|
* @param SolutionInterface $solution
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function addSolution(SolutionInterface $solution): static
|
||||||
|
{
|
||||||
|
$this->solutions[] = $solution;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all solutions
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getSolutions(): array
|
||||||
|
{
|
||||||
|
return $this->solutions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current element
|
||||||
|
*
|
||||||
|
* @return SolutionInterface
|
||||||
|
*/
|
||||||
|
public function current(): SolutionInterface
|
||||||
|
{
|
||||||
|
return $this->solutions[$this->iterator];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move forward to next element
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function next(): void
|
||||||
|
{
|
||||||
|
$this->iterator++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the key of the current element
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function key(): int
|
||||||
|
{
|
||||||
|
return $this->iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if current position is valid
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function valid(): bool
|
||||||
|
{
|
||||||
|
return array_key_exists($this->iterator, $this->solutions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rewind the Iterator to the first element
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function rewind(): void
|
||||||
|
{
|
||||||
|
$this->iterator = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count elements of an object
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function count(): int
|
||||||
|
{
|
||||||
|
return count($this->solutions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether an offset exists
|
||||||
|
*
|
||||||
|
* @param mixed $offset
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function offsetExists(mixed $offset): bool
|
||||||
|
{
|
||||||
|
return isset($this->solutions[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset to retrieve
|
||||||
|
*
|
||||||
|
* @param mixed $offset
|
||||||
|
* @return SolutionInterface
|
||||||
|
*/
|
||||||
|
public function offsetGet(mixed $offset): SolutionInterface
|
||||||
|
{
|
||||||
|
return $this->solutions[$offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset to set
|
||||||
|
*
|
||||||
|
* @param mixed $offset
|
||||||
|
* @param SolutionInterface $value
|
||||||
|
*/
|
||||||
|
public function offsetSet(mixed $offset, mixed $value): void
|
||||||
|
{
|
||||||
|
$this->solutions[$offset] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset to unset
|
||||||
|
*
|
||||||
|
* @param mixed $offset
|
||||||
|
*/
|
||||||
|
public function offsetUnset(mixed $offset): void
|
||||||
|
{
|
||||||
|
unset($this->solutions[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function jsonSerialize(): array
|
||||||
|
{
|
||||||
|
return array_merge(parent::jsonSerialize(), [
|
||||||
|
"solutions" => $this->getSolutions()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
38
src/Analysis/ProblemInterface.php
Normal file
38
src/Analysis/ProblemInterface.php
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Analysis;
|
||||||
|
|
||||||
|
use ArrayAccess;
|
||||||
|
use Countable;
|
||||||
|
use Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface ProblemInterface
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Analysis
|
||||||
|
*/
|
||||||
|
interface ProblemInterface extends Iterator, Countable, ArrayAccess, InsightInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Set all solutions at once in an array replacing the current solutions
|
||||||
|
*
|
||||||
|
* @param SolutionInterface[] $solutions
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setSolutions(array $solutions = []): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a solution
|
||||||
|
*
|
||||||
|
* @param SolutionInterface $solution
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function addSolution(SolutionInterface $solution): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all solutions
|
||||||
|
*
|
||||||
|
* @return SolutionInterface[]
|
||||||
|
*/
|
||||||
|
public function getSolutions(): array;
|
||||||
|
}
|
||||||
29
src/Analysis/Solution.php
Normal file
29
src/Analysis/Solution.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Analysis;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Solution
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Analysis
|
||||||
|
*/
|
||||||
|
abstract class Solution implements SolutionInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return $this->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function jsonSerialize(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'message' => $this->getMessage()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/Analysis/SolutionInterface.php
Normal file
21
src/Analysis/SolutionInterface.php
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Analysis;
|
||||||
|
|
||||||
|
use JsonSerializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface SolutionInterface
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Analysis
|
||||||
|
*/
|
||||||
|
interface SolutionInterface extends JsonSerializable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the solution as a human-readable message
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getMessage(): string;
|
||||||
|
public function __toString(): string;
|
||||||
|
}
|
||||||
149
src/Detective/Detective.php
Normal file
149
src/Detective/Detective.php
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Detective;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\DetectableLogInterface;
|
||||||
|
use Aternos\Codex\Log\File\LogFileInterface;
|
||||||
|
use Aternos\Codex\Log\Log;
|
||||||
|
use Aternos\Codex\Log\LogInterface;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Detective
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Detective
|
||||||
|
*/
|
||||||
|
class Detective implements DetectiveInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var class-string<LogInterface>[]
|
||||||
|
*/
|
||||||
|
protected array $possibleLogClasses = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var class-string<LogInterface>
|
||||||
|
*/
|
||||||
|
protected string $defaultLogClass = Log::class;
|
||||||
|
|
||||||
|
protected ?LogFileInterface $logFile = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set possible log classes
|
||||||
|
*
|
||||||
|
* Every class must implement DetectableLogInterface
|
||||||
|
*
|
||||||
|
* @param class-string<LogInterface>[] $logClasses
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setPossibleLogClasses(array $logClasses): static
|
||||||
|
{
|
||||||
|
$this->possibleLogClasses = [];
|
||||||
|
foreach ($logClasses as $logClass) {
|
||||||
|
$this->addPossibleLogClass($logClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a possible insight class
|
||||||
|
*
|
||||||
|
* The class must implement DetectableLogInterface
|
||||||
|
*
|
||||||
|
* @param class-string<LogInterface> $logClass
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function addPossibleLogClass(string $logClass): static
|
||||||
|
{
|
||||||
|
if (!is_subclass_of($logClass, DetectableLogInterface::class)) {
|
||||||
|
throw new InvalidArgumentException("Class " . $logClass . " does not implement " . DetectableLogInterface::class . ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->possibleLogClasses[] = $logClass;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add all possible log classes from another detective
|
||||||
|
*
|
||||||
|
* @param DetectiveInterface $detective
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function addDetective(DetectiveInterface $detective): static
|
||||||
|
{
|
||||||
|
foreach ($detective->getPossibleLogClasses() as $logClass) {
|
||||||
|
$this->addPossibleLogClass($logClass);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getPossibleLogClasses(): array
|
||||||
|
{
|
||||||
|
return $this->possibleLogClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the log file
|
||||||
|
*
|
||||||
|
* @param LogFileInterface $logFile
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLogFile(LogFileInterface $logFile): static
|
||||||
|
{
|
||||||
|
$this->logFile = $logFile;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect a log type out of possible classes by using detector
|
||||||
|
*
|
||||||
|
* @return LogInterface
|
||||||
|
*/
|
||||||
|
public function detect(): LogInterface
|
||||||
|
{
|
||||||
|
$detectionResults = [];
|
||||||
|
foreach ($this->possibleLogClasses as $possibleLogClass) {
|
||||||
|
/** @var DetectableLogInterface $possibleLogClass */
|
||||||
|
$detectors = $possibleLogClass::getDetectors();
|
||||||
|
foreach ($detectors as $detector) {
|
||||||
|
if (!$detector instanceof DetectorInterface) {
|
||||||
|
throw new InvalidArgumentException("Class " . get_class($detector) . " does not implement " . DetectorInterface::class . ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
$detector->setLogFile($this->logFile);
|
||||||
|
$result = $detector->detect();
|
||||||
|
if ($result === true) {
|
||||||
|
return (new $possibleLogClass())->setLogFile($this->logFile);
|
||||||
|
}
|
||||||
|
if ($result === false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!is_numeric($result) || $result < 0 || $result > 1) {
|
||||||
|
throw new InvalidArgumentException("Detector " . get_class($detector) . " returned " . var_export($result));
|
||||||
|
}
|
||||||
|
$detectionResults[] = ["class" => $possibleLogClass, "result" => $result];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($detectionResults) === 0) {
|
||||||
|
return (new $this->defaultLogClass())->setLogFile($this->logFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
usort($detectionResults, function ($a, $b) {
|
||||||
|
if ($a["result"] < $b["result"]) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($a["result"] > $b["result"]) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
return (new $detectionResults[0]["class"]())->setLogFile($this->logFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
56
src/Detective/DetectiveInterface.php
Normal file
56
src/Detective/DetectiveInterface.php
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Detective;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\File\LogFileInterface;
|
||||||
|
use Aternos\Codex\Log\LogInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface DetectiveInterface
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Detective
|
||||||
|
*/
|
||||||
|
interface DetectiveInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Set possible log classes
|
||||||
|
*
|
||||||
|
* Every class must implement DetectableLogInterface
|
||||||
|
*
|
||||||
|
* @param class-string<LogInterface>[] $logClasses
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setPossibleLogClasses(array $logClasses): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a possible log class
|
||||||
|
*
|
||||||
|
* The class must implement DetectableLogInterface
|
||||||
|
*
|
||||||
|
* @param class-string<LogInterface> $logClass
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function addPossibleLogClass(string $logClass): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all possible log classes
|
||||||
|
*
|
||||||
|
* @return class-string<LogInterface>[]
|
||||||
|
*/
|
||||||
|
public function getPossibleLogClasses(): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the log file
|
||||||
|
*
|
||||||
|
* @param LogFileInterface $logFile
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLogFile(LogFileInterface $logFile): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect a log type out of possible classes by using detector
|
||||||
|
*
|
||||||
|
* @return LogInterface
|
||||||
|
*/
|
||||||
|
public function detect(): LogInterface;
|
||||||
|
}
|
||||||
47
src/Detective/Detector.php
Normal file
47
src/Detective/Detector.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Detective;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\File\LogFileInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Detector
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Detective
|
||||||
|
*/
|
||||||
|
abstract class Detector implements DetectorInterface
|
||||||
|
{
|
||||||
|
protected ?LogFileInterface $logFile = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the log file
|
||||||
|
*
|
||||||
|
* @param LogFileInterface $logFile
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLogFile(LogFileInterface $logFile): static
|
||||||
|
{
|
||||||
|
$this->logFile = $logFile;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the log content as string
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function getLogContent(): string
|
||||||
|
{
|
||||||
|
return $this->logFile->getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the log content as array split by line
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
protected function getLogContentAsArray(): array
|
||||||
|
{
|
||||||
|
return explode(PHP_EOL, $this->getLogContent());
|
||||||
|
}
|
||||||
|
}
|
||||||
38
src/Detective/DetectorInterface.php
Normal file
38
src/Detective/DetectorInterface.php
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Detective;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\File\LogFileInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface DetectorInterface
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Detective
|
||||||
|
*/
|
||||||
|
interface DetectorInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Set the log file
|
||||||
|
*
|
||||||
|
* @param LogFileInterface $logFile
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLogFile(LogFileInterface $logFile): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect if the log matches
|
||||||
|
*
|
||||||
|
* Return true to directly force the detective to accept your result without considering any other detector
|
||||||
|
* Return false to force the detective to never use your result
|
||||||
|
* Return a number between 0 and 1 as probability for this detector
|
||||||
|
* Possible algorithm to get this number would be (matching lines) / (total lines)
|
||||||
|
*
|
||||||
|
* The detective decides which detector wins (and which related log class to use) in this order:
|
||||||
|
* return === true
|
||||||
|
* highest return
|
||||||
|
* default log
|
||||||
|
*
|
||||||
|
* @return bool|float
|
||||||
|
*/
|
||||||
|
public function detect(): float|bool;
|
||||||
|
}
|
||||||
37
src/Detective/LinePatternDetector.php
Normal file
37
src/Detective/LinePatternDetector.php
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Detective;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class LinePatternDetector
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Detective
|
||||||
|
*/
|
||||||
|
class LinePatternDetector extends PatternDetector
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Detect if the log matches
|
||||||
|
*
|
||||||
|
* Counts the lines matching the pattern and returns the percentage from 0-1 for the detective
|
||||||
|
*
|
||||||
|
* Returns false when no match is found
|
||||||
|
*
|
||||||
|
* @return bool|float
|
||||||
|
*/
|
||||||
|
public function detect(): bool|float
|
||||||
|
{
|
||||||
|
$lines = $this->getLogContentAsArray();
|
||||||
|
$matchingCounter = 0;
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
if (preg_match($this->pattern, $line) === 1) {
|
||||||
|
$matchingCounter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($matchingCounter === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $matchingCounter / count($lines);
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/Detective/MultiPatternDetector.php
Normal file
41
src/Detective/MultiPatternDetector.php
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Detective;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MultiPatternDetector can detect multiple patterns in a log and return true if all patterns are found
|
||||||
|
*/
|
||||||
|
class MultiPatternDetector extends Detector
|
||||||
|
{
|
||||||
|
protected array $patterns = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a pattern to the list of patterns to detect
|
||||||
|
*
|
||||||
|
* @param string $pattern
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function addPattern(string $pattern): static
|
||||||
|
{
|
||||||
|
$this->patterns[] = $pattern;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects if the log matches all patterns
|
||||||
|
*
|
||||||
|
* Returns true if all patterns are found, false otherwise
|
||||||
|
*
|
||||||
|
* @return bool|float
|
||||||
|
*/
|
||||||
|
public function detect(): bool|float
|
||||||
|
{
|
||||||
|
foreach ($this->patterns as $pattern) {
|
||||||
|
if (preg_match($pattern, $this->getLogContent()) !== 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/Detective/PatternDetector.php
Normal file
25
src/Detective/PatternDetector.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Detective;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class PatternDetector
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Detective
|
||||||
|
*/
|
||||||
|
abstract class PatternDetector extends Detector
|
||||||
|
{
|
||||||
|
protected ?string $pattern = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the matching pattern for one line
|
||||||
|
*
|
||||||
|
* @param string $pattern
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setPattern(string $pattern): static
|
||||||
|
{
|
||||||
|
$this->pattern = $pattern;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/Detective/SinglePatternDetector.php
Normal file
29
src/Detective/SinglePatternDetector.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Detective;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class SinglePatternDetector
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Detective
|
||||||
|
*/
|
||||||
|
class SinglePatternDetector extends PatternDetector
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Detect if the log matches
|
||||||
|
*
|
||||||
|
* Checks if the pattern matches anywhere in the log file
|
||||||
|
*
|
||||||
|
* Returns either true or false
|
||||||
|
*
|
||||||
|
* @return bool|float
|
||||||
|
*/
|
||||||
|
public function detect(): bool|float
|
||||||
|
{
|
||||||
|
if (preg_match($this->pattern, $this->getLogContent()) === 1) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
43
src/Detective/WeightedSinglePatternDetector.php
Normal file
43
src/Detective/WeightedSinglePatternDetector.php
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Detective;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class WeightedSinglePatternDetector
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Detective
|
||||||
|
*/
|
||||||
|
class WeightedSinglePatternDetector extends SinglePatternDetector
|
||||||
|
{
|
||||||
|
protected ?float $weight = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the weight that will be returned if the pattern matches
|
||||||
|
*
|
||||||
|
* @param float $weight
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setWeight(float $weight): static
|
||||||
|
{
|
||||||
|
$this->weight = $weight;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect if the log matches
|
||||||
|
*
|
||||||
|
* Checks if the pattern matches anywhere in the log file
|
||||||
|
*
|
||||||
|
* Returns either true or false
|
||||||
|
*
|
||||||
|
* @return bool|float
|
||||||
|
*/
|
||||||
|
public function detect(): bool|float
|
||||||
|
{
|
||||||
|
if (parent::detect()) {
|
||||||
|
return $this->weight;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
50
src/Log/AnalysableLog.php
Normal file
50
src/Log/AnalysableLog.php
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Log;
|
||||||
|
|
||||||
|
use Aternos\Codex\Analyser\AnalyserInterface;
|
||||||
|
use Aternos\Codex\Analysis\AnalysisInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class AnalysableLog
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Log
|
||||||
|
*/
|
||||||
|
abstract class AnalysableLog extends Log implements AnalysableLogInterface
|
||||||
|
{
|
||||||
|
protected ?AnalysisInterface $analysis = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Analyse a log file with an analyser
|
||||||
|
*
|
||||||
|
* Every log type should have a default analyser,
|
||||||
|
* but the $analyser argument can be used to override
|
||||||
|
* the default analyser
|
||||||
|
*
|
||||||
|
* @param AnalyserInterface|null $analyser
|
||||||
|
* @return AnalysisInterface
|
||||||
|
*/
|
||||||
|
public function analyse(?AnalyserInterface $analyser = null): AnalysisInterface
|
||||||
|
{
|
||||||
|
if ($this->analysis !== null) {
|
||||||
|
return $this->analysis;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($analyser === null) {
|
||||||
|
$analyser = static::getDefaultAnalyser();
|
||||||
|
}
|
||||||
|
|
||||||
|
$analyser->setLog($this);
|
||||||
|
return $this->analysis = $analyser->analyse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function jsonSerialize(): array
|
||||||
|
{
|
||||||
|
return array_merge(parent::jsonSerialize(), [
|
||||||
|
'analysis' => $this->analyse()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/Log/AnalysableLogInterface.php
Normal file
33
src/Log/AnalysableLogInterface.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Log;
|
||||||
|
|
||||||
|
use Aternos\Codex\Analyser\AnalyserInterface;
|
||||||
|
use Aternos\Codex\Analysis\AnalysisInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface AnalysableLogInterface
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Log
|
||||||
|
*/
|
||||||
|
interface AnalysableLogInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the default analyser
|
||||||
|
*
|
||||||
|
* @return AnalyserInterface
|
||||||
|
*/
|
||||||
|
public static function getDefaultAnalyser(): AnalyserInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Analyse a log file with an analyser
|
||||||
|
*
|
||||||
|
* Every log type should have a default analyser,
|
||||||
|
* but the $analyser argument can be used to override
|
||||||
|
* the default analyser
|
||||||
|
*
|
||||||
|
* @param AnalyserInterface|null $analyser
|
||||||
|
* @return AnalysisInterface
|
||||||
|
*/
|
||||||
|
public function analyse(?AnalyserInterface $analyser = null): AnalysisInterface;
|
||||||
|
}
|
||||||
20
src/Log/DetectableLogInterface.php
Normal file
20
src/Log/DetectableLogInterface.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Log;
|
||||||
|
|
||||||
|
use Aternos\Codex\Detective\DetectorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface DetectableLogInterface
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Log
|
||||||
|
*/
|
||||||
|
interface DetectableLogInterface extends LogInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get an array of detectors matching DetectorInterface
|
||||||
|
*
|
||||||
|
* @return DetectorInterface[]
|
||||||
|
*/
|
||||||
|
public static function getDetectors(): array;
|
||||||
|
}
|
||||||
244
src/Log/Entry.php
Normal file
244
src/Log/Entry.php
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Entry
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Log
|
||||||
|
*/
|
||||||
|
class Entry implements EntryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var LineInterface[]
|
||||||
|
*/
|
||||||
|
protected array $lines = [];
|
||||||
|
protected ?LevelInterface $level = null;
|
||||||
|
protected ?int $time = null;
|
||||||
|
protected ?string $prefix = null;
|
||||||
|
protected int $iterator = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set all lines at once in an array replacing the current lines
|
||||||
|
*
|
||||||
|
* @param LineInterface[] $lines
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLines(array $lines = []): static
|
||||||
|
{
|
||||||
|
$this->lines = $lines;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a line
|
||||||
|
*
|
||||||
|
* @param LineInterface $line
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function addLine(LineInterface $line): static
|
||||||
|
{
|
||||||
|
$this->lines[] = $line;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all lines
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getLines(): array
|
||||||
|
{
|
||||||
|
return $this->lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the log level of the entry
|
||||||
|
*
|
||||||
|
* @param LevelInterface $level
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLevel(LevelInterface $level): static
|
||||||
|
{
|
||||||
|
$this->level = $level;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the log level of the entry
|
||||||
|
*
|
||||||
|
* @return LevelInterface
|
||||||
|
*/
|
||||||
|
public function getLevel(): LevelInterface
|
||||||
|
{
|
||||||
|
return $this->level ?? Level::INFO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the timestamp of the entry
|
||||||
|
*
|
||||||
|
* @param int $time
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setTime(int $time): static
|
||||||
|
{
|
||||||
|
$this->time = $time;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the timestamp of the entry
|
||||||
|
*
|
||||||
|
* @return int|null
|
||||||
|
*/
|
||||||
|
public function getTime(): ?int
|
||||||
|
{
|
||||||
|
return $this->time;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the prefix
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setPrefix(string $prefix): static
|
||||||
|
{
|
||||||
|
$this->prefix = $prefix;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the prefix
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getPrefix(): ?string
|
||||||
|
{
|
||||||
|
return $this->prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current element
|
||||||
|
*
|
||||||
|
* @return Line
|
||||||
|
*/
|
||||||
|
public function current(): Line
|
||||||
|
{
|
||||||
|
return $this->lines[$this->iterator];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move forward to next element
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function next(): void
|
||||||
|
{
|
||||||
|
$this->iterator++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the key of the current element
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function key(): int
|
||||||
|
{
|
||||||
|
return $this->iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if current position is valid
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function valid(): bool
|
||||||
|
{
|
||||||
|
return array_key_exists($this->iterator, $this->lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rewind the Iterator to the first element
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function rewind(): void
|
||||||
|
{
|
||||||
|
$this->iterator = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count elements of an object
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function count(): int
|
||||||
|
{
|
||||||
|
return count($this->lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether an offset exists
|
||||||
|
*
|
||||||
|
* @param mixed $offset
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function offsetExists(mixed $offset): bool
|
||||||
|
{
|
||||||
|
return isset($this->lines[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset to retrieve
|
||||||
|
*
|
||||||
|
* @param mixed $offset
|
||||||
|
* @return LineInterface
|
||||||
|
*/
|
||||||
|
public function offsetGet(mixed $offset): LineInterface
|
||||||
|
{
|
||||||
|
return $this->lines[$offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset to set
|
||||||
|
*
|
||||||
|
* @param mixed $offset
|
||||||
|
* @param LineInterface $value
|
||||||
|
*/
|
||||||
|
public function offsetSet(mixed $offset, mixed $value): void
|
||||||
|
{
|
||||||
|
$this->lines[$offset] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset to unset
|
||||||
|
*
|
||||||
|
* @param mixed $offset
|
||||||
|
*/
|
||||||
|
public function offsetUnset(mixed $offset): void
|
||||||
|
{
|
||||||
|
unset($this->lines[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return implode("\n", $this->getLines());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function jsonSerialize(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'level' => $this->getLevel(),
|
||||||
|
'time' => $this->getTime(),
|
||||||
|
'prefix' => $this->getPrefix(),
|
||||||
|
'lines' => $this->getLines()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
67
src/Log/EntryInterface.php
Normal file
67
src/Log/EntryInterface.php
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Log;
|
||||||
|
|
||||||
|
use ArrayAccess;
|
||||||
|
use Countable;
|
||||||
|
use Iterator;
|
||||||
|
use JsonSerializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface EntryInterface
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Log
|
||||||
|
*/
|
||||||
|
interface EntryInterface extends Iterator, Countable, ArrayAccess, JsonSerializable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Set all lines at once in an array replacing the current lines
|
||||||
|
*
|
||||||
|
* @param LineInterface[] $lines
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLines(array $lines = []): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a line
|
||||||
|
*
|
||||||
|
* @param LineInterface $line
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function addLine(LineInterface $line): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all lines
|
||||||
|
*
|
||||||
|
* @return LineInterface[]
|
||||||
|
*/
|
||||||
|
public function getLines(): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current element
|
||||||
|
*
|
||||||
|
* @return LineInterface
|
||||||
|
*/
|
||||||
|
public function current(): LineInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset to set
|
||||||
|
*
|
||||||
|
* @param mixed $offset
|
||||||
|
* @param LineInterface $value
|
||||||
|
*/
|
||||||
|
public function offsetSet(mixed $offset, mixed $value): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset to retrieve
|
||||||
|
*
|
||||||
|
* @param mixed $offset
|
||||||
|
* @return LineInterface
|
||||||
|
*/
|
||||||
|
public function offsetGet(mixed $offset): LineInterface;
|
||||||
|
}
|
||||||
23
src/Log/File/LogFile.php
Normal file
23
src/Log/File/LogFile.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Log\File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class LogFile
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Log\File
|
||||||
|
*/
|
||||||
|
abstract class LogFile implements LogFileInterface
|
||||||
|
{
|
||||||
|
protected ?string $content = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the log file content
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getContent(): string
|
||||||
|
{
|
||||||
|
return $this->content;
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/Log/File/LogFileInterface.php
Normal file
18
src/Log/File/LogFileInterface.php
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Log\File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface LogFileInterface
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Log\File
|
||||||
|
*/
|
||||||
|
interface LogFileInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the log file content
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getContent(): string;
|
||||||
|
}
|
||||||
27
src/Log/File/PathLogFile.php
Normal file
27
src/Log/File/PathLogFile.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Log\File;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class PathLogFile
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Log\File
|
||||||
|
*/
|
||||||
|
class PathLogFile extends LogFile
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* PathLogFile constructor.
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
*/
|
||||||
|
public function __construct(string $path)
|
||||||
|
{
|
||||||
|
if (!file_exists($path)) {
|
||||||
|
throw new InvalidArgumentException("File '" . $path . "' not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->content = file_get_contents($path);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/Log/File/StreamLogFile.php
Normal file
30
src/Log/File/StreamLogFile.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Log\File;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class StreamLogFile
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Log\File
|
||||||
|
*/
|
||||||
|
class StreamLogFile extends LogFile
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* StreamLogFile constructor.
|
||||||
|
*
|
||||||
|
* @param resource $streamResource
|
||||||
|
*/
|
||||||
|
public function __construct($streamResource)
|
||||||
|
{
|
||||||
|
if (!is_resource($streamResource)) {
|
||||||
|
throw new InvalidArgumentException("Stream argument is not a resource");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->content = '';
|
||||||
|
while (!feof($streamResource)) {
|
||||||
|
$this->content .= fread($streamResource, 8192);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/Log/File/StringLogFile.php
Normal file
21
src/Log/File/StringLogFile.php
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Log\File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class StringLogFile
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Log\File
|
||||||
|
*/
|
||||||
|
class StringLogFile extends LogFile
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* StringLogFile constructor.
|
||||||
|
*
|
||||||
|
* @param string $string
|
||||||
|
*/
|
||||||
|
public function __construct(string $string)
|
||||||
|
{
|
||||||
|
$this->content = $string;
|
||||||
|
}
|
||||||
|
}
|
||||||
66
src/Log/Level.php
Normal file
66
src/Log/Level.php
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Log;
|
||||||
|
|
||||||
|
enum Level: int implements LevelInterface
|
||||||
|
{
|
||||||
|
case EMERGENCY = 0;
|
||||||
|
case ALERT = 1;
|
||||||
|
case CRITICAL = 2;
|
||||||
|
case ERROR = 3;
|
||||||
|
case WARNING = 4;
|
||||||
|
case NOTICE = 5;
|
||||||
|
case INFO = 6;
|
||||||
|
case DEBUG = 7;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $level
|
||||||
|
* @return Level
|
||||||
|
*/
|
||||||
|
public static function fromString(string $level): Level
|
||||||
|
{
|
||||||
|
return match (strtolower($level)) {
|
||||||
|
"emergency" => Level::EMERGENCY,
|
||||||
|
"alert" => Level::ALERT,
|
||||||
|
"critical", "severe", "fatal" => Level::CRITICAL,
|
||||||
|
"error", "stderr" => Level::ERROR,
|
||||||
|
"warning", "warn" => Level::WARNING,
|
||||||
|
"notice", "fine" => Level::NOTICE,
|
||||||
|
"debug", "finer", "finest" => Level::DEBUG,
|
||||||
|
default => Level::INFO
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function asString(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
Level::EMERGENCY => "emergency",
|
||||||
|
Level::ALERT => "alert",
|
||||||
|
Level::CRITICAL => "critical",
|
||||||
|
Level::ERROR => "error",
|
||||||
|
Level::WARNING => "warning",
|
||||||
|
Level::NOTICE => "notice",
|
||||||
|
Level::INFO => "info",
|
||||||
|
Level::DEBUG => "debug"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function asInt(): int
|
||||||
|
{
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function jsonSerialize(): int
|
||||||
|
{
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/Log/LevelInterface.php
Normal file
24
src/Log/LevelInterface.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Log;
|
||||||
|
|
||||||
|
use JsonSerializable;
|
||||||
|
|
||||||
|
interface LevelInterface extends JsonSerializable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string $level
|
||||||
|
* @return LevelInterface
|
||||||
|
*/
|
||||||
|
public static function fromString(string $level): LevelInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function asString(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function asInt(): int;
|
||||||
|
}
|
||||||
84
src/Log/Line.php
Normal file
84
src/Log/Line.php
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Line
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Log
|
||||||
|
*/
|
||||||
|
class Line implements LineInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param int $number
|
||||||
|
* @param string $text
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
protected int $number,
|
||||||
|
protected string $text)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the text of the line
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setText(string $text): static
|
||||||
|
{
|
||||||
|
$this->text = $text;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the text of the line
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getText(): string
|
||||||
|
{
|
||||||
|
return $this->text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the line number
|
||||||
|
*
|
||||||
|
* @param int $number
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setNumber(int $number): static
|
||||||
|
{
|
||||||
|
$this->number = $number;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the line number
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getNumber(): int
|
||||||
|
{
|
||||||
|
return $this->number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return $this->getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function jsonSerialize(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'number' => $this->getNumber(),
|
||||||
|
'content' => $this->getText()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
48
src/Log/LineInterface.php
Normal file
48
src/Log/LineInterface.php
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Log;
|
||||||
|
|
||||||
|
use JsonSerializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface LineInterface
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Log
|
||||||
|
*/
|
||||||
|
interface LineInterface extends JsonSerializable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Set the text of the line
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setText(string $text): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the text of the line
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getText(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the line number
|
||||||
|
*
|
||||||
|
* @param int $number
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setNumber(int $number): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the line number
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getNumber(): int;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString(): string;
|
||||||
|
}
|
||||||
253
src/Log/Log.php
Normal file
253
src/Log/Log.php
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Log;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\File\LogFileInterface;
|
||||||
|
use Aternos\Codex\Parser\DefaultParser;
|
||||||
|
use Aternos\Codex\Parser\ParserInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Log
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Log
|
||||||
|
*/
|
||||||
|
class Log implements LogInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var EntryInterface[]
|
||||||
|
*/
|
||||||
|
protected array $entries = [];
|
||||||
|
protected int $iterator = 0;
|
||||||
|
protected ?LogFileInterface $logFile = null;
|
||||||
|
protected bool $includeEntries = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the default parser
|
||||||
|
*
|
||||||
|
* @return ParserInterface
|
||||||
|
*/
|
||||||
|
public static function getDefaultParser(): ParserInterface
|
||||||
|
{
|
||||||
|
return new DefaultParser();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the log file
|
||||||
|
*
|
||||||
|
* @param LogFileInterface $logFile
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLogFile(LogFileInterface $logFile): static
|
||||||
|
{
|
||||||
|
$this->logFile = $logFile;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the log file
|
||||||
|
*
|
||||||
|
* @return LogFileInterface
|
||||||
|
*/
|
||||||
|
public function getLogfile(): LogFileInterface
|
||||||
|
{
|
||||||
|
return $this->logFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a log file with a parser
|
||||||
|
*
|
||||||
|
* Every log type should have a default parser,
|
||||||
|
* but the $parser argument can be used to override
|
||||||
|
* the default parser
|
||||||
|
*
|
||||||
|
* @param ParserInterface|null $parser
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function parse(?ParserInterface $parser = null): static
|
||||||
|
{
|
||||||
|
if ($parser === null) {
|
||||||
|
$parser = static::getDefaultParser();
|
||||||
|
}
|
||||||
|
|
||||||
|
$parser->setLog($this)->parse();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set all entries of the log at once replacing the current entries
|
||||||
|
*
|
||||||
|
* @param EntryInterface[] $entries
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setEntries(array $entries = []): static
|
||||||
|
{
|
||||||
|
$this->entries = $entries;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an entry to the log
|
||||||
|
*
|
||||||
|
* @param EntryInterface $entry
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function addEntry(EntryInterface $entry): static
|
||||||
|
{
|
||||||
|
$this->entries[] = $entry;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all entries of the log
|
||||||
|
*
|
||||||
|
* @return EntryInterface[]
|
||||||
|
*/
|
||||||
|
public function getEntries(): array
|
||||||
|
{
|
||||||
|
return $this->entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current element
|
||||||
|
*
|
||||||
|
* @return EntryInterface
|
||||||
|
*/
|
||||||
|
public function current(): EntryInterface
|
||||||
|
{
|
||||||
|
return $this->entries[$this->iterator];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move forward to next element
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function next(): void
|
||||||
|
{
|
||||||
|
$this->iterator++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the key of the current element
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function key(): int
|
||||||
|
{
|
||||||
|
return $this->iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if current position is valid
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function valid(): bool
|
||||||
|
{
|
||||||
|
return array_key_exists($this->iterator, $this->entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rewind the Iterator to the first element
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function rewind(): void
|
||||||
|
{
|
||||||
|
$this->iterator = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count elements of an object
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function count(): int
|
||||||
|
{
|
||||||
|
return count($this->entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether an offset exists
|
||||||
|
*
|
||||||
|
* @param mixed $offset
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function offsetExists(mixed $offset): bool
|
||||||
|
{
|
||||||
|
return isset($this->entries[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset to retrieve
|
||||||
|
*
|
||||||
|
* @param mixed $offset
|
||||||
|
* @return EntryInterface
|
||||||
|
*/
|
||||||
|
public function offsetGet(mixed $offset): EntryInterface
|
||||||
|
{
|
||||||
|
return $this->entries[$offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset to set
|
||||||
|
*
|
||||||
|
* @param mixed $offset
|
||||||
|
* @param EntryInterface $value
|
||||||
|
*/
|
||||||
|
public function offsetSet(mixed $offset, mixed $value): void
|
||||||
|
{
|
||||||
|
$this->entries[$offset] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset to unset
|
||||||
|
*
|
||||||
|
* @param mixed $offset
|
||||||
|
*/
|
||||||
|
public function offsetUnset(mixed $offset): void
|
||||||
|
{
|
||||||
|
unset($this->entries[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return implode("\n", $this->getEntries());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $includeEntries
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setIncludeEntries(bool $includeEntries): static
|
||||||
|
{
|
||||||
|
$this->includeEntries = $includeEntries;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function jsonSerialize(): array
|
||||||
|
{
|
||||||
|
if (!$this->includeEntries) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
"entries" => $this->getEntries()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getTitle(): string
|
||||||
|
{
|
||||||
|
return "Log";
|
||||||
|
}
|
||||||
|
}
|
||||||
110
src/Log/LogInterface.php
Normal file
110
src/Log/LogInterface.php
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Log;
|
||||||
|
|
||||||
|
use ArrayAccess;
|
||||||
|
use Aternos\Codex\Log\File\LogFileInterface;
|
||||||
|
use Aternos\Codex\Parser\ParserInterface;
|
||||||
|
use Countable;
|
||||||
|
use Iterator;
|
||||||
|
use JsonSerializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface LogInterface
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Log
|
||||||
|
*/
|
||||||
|
interface LogInterface extends Iterator, Countable, ArrayAccess, JsonSerializable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the default parser
|
||||||
|
*
|
||||||
|
* @return ParserInterface
|
||||||
|
*/
|
||||||
|
public static function getDefaultParser(): ParserInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the log file
|
||||||
|
*
|
||||||
|
* @param LogFileInterface $logFile
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLogFile(LogFileInterface $logFile): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the log file
|
||||||
|
*
|
||||||
|
* @return LogFileInterface
|
||||||
|
*/
|
||||||
|
public function getLogFile(): LogFileInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a human-readable title for the log
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getTitle(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a log file with a parser
|
||||||
|
*
|
||||||
|
* Every log type should have a default parser,
|
||||||
|
* but the $parser argument can be used to override
|
||||||
|
* the default parser
|
||||||
|
*
|
||||||
|
* @param ParserInterface|null $parser
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function parse(?ParserInterface $parser = null): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set all entries of the log at once replacing the current entries
|
||||||
|
*
|
||||||
|
* @param EntryInterface[] $entries
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setEntries(array $entries = []): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an entry to the log
|
||||||
|
*
|
||||||
|
* @param EntryInterface $entry
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function addEntry(EntryInterface $entry): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all entries of the log
|
||||||
|
*
|
||||||
|
* @return EntryInterface[]
|
||||||
|
*/
|
||||||
|
public function getEntries(): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current element
|
||||||
|
*
|
||||||
|
* @return EntryInterface
|
||||||
|
*/
|
||||||
|
public function current(): EntryInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset to set
|
||||||
|
*
|
||||||
|
* @param mixed $offset
|
||||||
|
* @param EntryInterface $value
|
||||||
|
*/
|
||||||
|
public function offsetSet(mixed $offset, mixed $value): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset to retrieve
|
||||||
|
*
|
||||||
|
* @param mixed $offset
|
||||||
|
* @return EntryInterface
|
||||||
|
*/
|
||||||
|
public function offsetGet(mixed $offset): EntryInterface;
|
||||||
|
}
|
||||||
26
src/Parser/DefaultParser.php
Normal file
26
src/Parser/DefaultParser.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Parser;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\Entry;
|
||||||
|
use Aternos\Codex\Log\Line;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class DefaultParser
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Parser
|
||||||
|
*/
|
||||||
|
class DefaultParser extends Parser
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Parse a log from resource to Log object
|
||||||
|
*/
|
||||||
|
public function parse(): void
|
||||||
|
{
|
||||||
|
foreach ($this->getLogContentAsArray() as $number => $logLineString) {
|
||||||
|
$this->log->addEntry((new Entry())
|
||||||
|
->addLine(new Line($number + 1, $logLineString))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
src/Parser/Parser.php
Normal file
47
src/Parser/Parser.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Parser;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\LogInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Parser
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Parser
|
||||||
|
*/
|
||||||
|
abstract class Parser implements ParserInterface
|
||||||
|
{
|
||||||
|
protected ?LogInterface $log = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the output log object
|
||||||
|
*
|
||||||
|
* @param LogInterface $log
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLog(LogInterface $log): static
|
||||||
|
{
|
||||||
|
$this->log = $log;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the log content as string
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function getLogContent(): string
|
||||||
|
{
|
||||||
|
return $this->log->getLogFile()->getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the log content as array split by line
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
protected function getLogContentAsArray(): array
|
||||||
|
{
|
||||||
|
return explode(PHP_EOL, $this->getLogContent());
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/Parser/ParserInterface.php
Normal file
26
src/Parser/ParserInterface.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Parser;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\LogInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface ParserInterface
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Parser
|
||||||
|
*/
|
||||||
|
interface ParserInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Set the output log object
|
||||||
|
*
|
||||||
|
* @param LogInterface $log
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLog(LogInterface $log): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a log from resource to Log object
|
||||||
|
*/
|
||||||
|
public function parse(): void;
|
||||||
|
}
|
||||||
183
src/Parser/PatternParser.php
Normal file
183
src/Parser/PatternParser.php
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Parser;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\Entry;
|
||||||
|
use Aternos\Codex\Log\EntryInterface;
|
||||||
|
use Aternos\Codex\Log\Level;
|
||||||
|
use Aternos\Codex\Log\LevelInterface;
|
||||||
|
use Aternos\Codex\Log\Line;
|
||||||
|
use DateTime;
|
||||||
|
use DateTimeZone;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class PatternParser
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Parser
|
||||||
|
*/
|
||||||
|
class PatternParser extends Parser
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Match constants, see setMatches()
|
||||||
|
*/
|
||||||
|
public const string TIME = "time";
|
||||||
|
public const string LEVEL = "level";
|
||||||
|
public const string PREFIX = "prefix";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var class-string<EntryInterface>
|
||||||
|
*/
|
||||||
|
protected string $entryClass = Entry::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @noinspection PhpDocFieldTypeMismatchInspection
|
||||||
|
* @var class-string<LevelInterface>|LevelInterface
|
||||||
|
*/
|
||||||
|
protected string $levelClass = Level::class;
|
||||||
|
|
||||||
|
protected ?string $pattern = null;
|
||||||
|
protected array $matches = [];
|
||||||
|
protected ?string $timeFormat = null;
|
||||||
|
protected ?DateTimeZone $timeZone = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the entry pattern
|
||||||
|
*
|
||||||
|
* Every line matching this pattern is defined as
|
||||||
|
* new entry, all other lines are added to the
|
||||||
|
* previous entry
|
||||||
|
*
|
||||||
|
* @param string $pattern
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setPattern(string $pattern): static
|
||||||
|
{
|
||||||
|
$this->pattern = $pattern;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the entry pattern
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getPattern(): string
|
||||||
|
{
|
||||||
|
return $this->pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the array of match constants
|
||||||
|
*
|
||||||
|
* The position/key in the array defines
|
||||||
|
* the position of the matching capturing
|
||||||
|
* group in the $pattern
|
||||||
|
*
|
||||||
|
* @param array $matches
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setMatches(array $matches): static
|
||||||
|
{
|
||||||
|
$this->matches = $matches;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the time format
|
||||||
|
*
|
||||||
|
* Time is parsed with the DateTime::createFromFormat() function,
|
||||||
|
* see this for format information:
|
||||||
|
*
|
||||||
|
* http://php.net/manual/en/datetime.createfromformat.php
|
||||||
|
*
|
||||||
|
* @param string $timeFormat
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setTimeFormat(string $timeFormat): static
|
||||||
|
{
|
||||||
|
$this->timeFormat = $timeFormat;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the time zone
|
||||||
|
*
|
||||||
|
* Optional, uses OS timezone otherwise
|
||||||
|
*
|
||||||
|
* @param DateTimeZone $timeZone
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setTimezone(DateTimeZone $timeZone): static
|
||||||
|
{
|
||||||
|
$this->timeZone = $timeZone;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a log from resource to Log object
|
||||||
|
*/
|
||||||
|
public function parse(): void
|
||||||
|
{
|
||||||
|
foreach ($this->getLogContentAsArray() as $number => $lineString) {
|
||||||
|
$line = new Line($number + 1, $lineString);
|
||||||
|
$result = preg_match($this->pattern, $lineString, $matches);
|
||||||
|
if ($result !== 1) {
|
||||||
|
if (!isset($entry)) {
|
||||||
|
/** @var Entry $entry */
|
||||||
|
$entry = new $this->entryClass();
|
||||||
|
$this->log->addEntry($entry);
|
||||||
|
}
|
||||||
|
$entry->addLine($line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var Entry $entry */
|
||||||
|
$entry = new $this->entryClass();
|
||||||
|
$this->log->addEntry($entry);
|
||||||
|
foreach ($matches as $key => $match) {
|
||||||
|
if ($key === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$matchKey = $key - 1;
|
||||||
|
if (!isset($this->matches[$matchKey])) {
|
||||||
|
throw new InvalidArgumentException("More matches found in string than defined in PatternParser::setMatches().");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->parseEntryMatch($entry, $this->matches[$matchKey], $match);
|
||||||
|
}
|
||||||
|
$entry->addLine($line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse an entry match
|
||||||
|
*
|
||||||
|
* Overwrite this function to add more different
|
||||||
|
* match types and call the parent function (this function)
|
||||||
|
* if you don't know the match type (default in a switch)
|
||||||
|
*
|
||||||
|
* @param Entry $entry
|
||||||
|
* @param string $matchType One of the match constants
|
||||||
|
* @param string $matchString
|
||||||
|
*/
|
||||||
|
protected function parseEntryMatch(Entry $entry, string $matchType, string $matchString): void
|
||||||
|
{
|
||||||
|
switch ($matchType) {
|
||||||
|
case static::TIME:
|
||||||
|
$date = DateTime::createFromFormat($this->timeFormat, $matchString, $this->timeZone);
|
||||||
|
if ($date) {
|
||||||
|
$entry->setTime($date->getTimestamp());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case static::LEVEL:
|
||||||
|
$entry->setLevel($this->levelClass::fromString($matchString));
|
||||||
|
break;
|
||||||
|
case static::PREFIX:
|
||||||
|
$entry->setPrefix($matchString);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new InvalidArgumentException("Match type '" . $matchType . "' is not defined.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/Printer/DefaultPrinter.php
Normal file
24
src/Printer/DefaultPrinter.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Printer;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\LineInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class DefaultPrinter
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Printer
|
||||||
|
*/
|
||||||
|
class DefaultPrinter extends Printer
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Print a line
|
||||||
|
*
|
||||||
|
* @param LineInterface $line
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function printLine(LineInterface $line): string
|
||||||
|
{
|
||||||
|
return $line->getText() . PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/Printer/ModifiableDefaultPrinter.php
Normal file
24
src/Printer/ModifiableDefaultPrinter.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Printer;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\LineInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ModifiableDefaultPrinter
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Printer
|
||||||
|
*/
|
||||||
|
class ModifiableDefaultPrinter extends ModifiablePrinter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Print a line
|
||||||
|
*
|
||||||
|
* @param LineInterface $line
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function printLine(LineInterface $line): string
|
||||||
|
{
|
||||||
|
return $this->runModifications($line->getText()) . PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/Printer/ModifiablePrinter.php
Normal file
59
src/Printer/ModifiablePrinter.php
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Printer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ModifiablePrinter
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Printer
|
||||||
|
*/
|
||||||
|
abstract class ModifiablePrinter extends Printer implements ModifiablePrinterInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var ModificationInterface[]
|
||||||
|
*/
|
||||||
|
protected array $modifications = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set all modifications replacing the current modifications
|
||||||
|
*
|
||||||
|
* @param ModificationInterface[] $modifications
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setModifications(array $modifications): static
|
||||||
|
{
|
||||||
|
$this->modifications = [];
|
||||||
|
foreach ($modifications as $modification) {
|
||||||
|
$this->addModification($modification);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a modification
|
||||||
|
*
|
||||||
|
* @param ModificationInterface $modification
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function addModification(ModificationInterface $modification): static
|
||||||
|
{
|
||||||
|
$this->modifications[] = $modification;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the set modifications for a string
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function runModifications(string $text): string
|
||||||
|
{
|
||||||
|
foreach ($this->modifications as $modification) {
|
||||||
|
$text = $modification->modify($text);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $text;
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/Printer/ModifiablePrinterInterface.php
Normal file
27
src/Printer/ModifiablePrinterInterface.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Printer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface ModifiablePrinterInterface
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Printer
|
||||||
|
*/
|
||||||
|
interface ModifiablePrinterInterface extends PrinterInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Set all modifications replacing the current modifications
|
||||||
|
*
|
||||||
|
* @param ModificationInterface[] $modifications
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setModifications(array $modifications): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a modification
|
||||||
|
*
|
||||||
|
* @param ModificationInterface $modification
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function addModification(ModificationInterface $modification): static;
|
||||||
|
}
|
||||||
13
src/Printer/Modification.php
Normal file
13
src/Printer/Modification.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Printer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Modification
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Printer
|
||||||
|
*/
|
||||||
|
abstract class Modification implements ModificationInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
19
src/Printer/ModificationInterface.php
Normal file
19
src/Printer/ModificationInterface.php
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Printer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface ModificationInterface
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Printer
|
||||||
|
*/
|
||||||
|
interface ModificationInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Modify the given string and return it
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function modify(string $text): string;
|
||||||
|
}
|
||||||
60
src/Printer/PatternModification.php
Normal file
60
src/Printer/PatternModification.php
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Printer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class PatternModification
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Printer
|
||||||
|
*/
|
||||||
|
class PatternModification extends Modification
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string $pattern
|
||||||
|
* @param string $replacement
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
protected string $pattern,
|
||||||
|
protected string $replacement)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the pattern
|
||||||
|
*
|
||||||
|
* See http://php.net/manual/de/function.preg-replace.php
|
||||||
|
*
|
||||||
|
* @param string $pattern
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setPattern(string $pattern): PatternModification
|
||||||
|
{
|
||||||
|
$this->pattern = $pattern;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the replacement string
|
||||||
|
*
|
||||||
|
* See http://php.net/manual/de/function.preg-replace.php
|
||||||
|
*
|
||||||
|
* @param string $replacement
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setReplacement(string $replacement): PatternModification
|
||||||
|
{
|
||||||
|
$this->replacement = $replacement;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modify the given string and return it
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function modify(string $text): string
|
||||||
|
{
|
||||||
|
return preg_replace($this->pattern, $this->replacement, $text);
|
||||||
|
}
|
||||||
|
}
|
||||||
99
src/Printer/Printer.php
Normal file
99
src/Printer/Printer.php
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Printer;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\EntryInterface;
|
||||||
|
use Aternos\Codex\Log\LineInterface;
|
||||||
|
use Aternos\Codex\Log\LogInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Printer
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Printer
|
||||||
|
*/
|
||||||
|
abstract class Printer implements PrinterInterface
|
||||||
|
{
|
||||||
|
protected ?LogInterface $log = null;
|
||||||
|
protected ?EntryInterface $entry = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the log
|
||||||
|
*
|
||||||
|
* @param LogInterface $log
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLog(LogInterface $log): static
|
||||||
|
{
|
||||||
|
$this->log = $log;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the entry
|
||||||
|
*
|
||||||
|
* @param EntryInterface $entry
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setEntry(EntryInterface $entry): static
|
||||||
|
{
|
||||||
|
$this->entry = $entry;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print the log
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function print(): string
|
||||||
|
{
|
||||||
|
if ($this->entry) {
|
||||||
|
return $this->printEntry();
|
||||||
|
} else {
|
||||||
|
return $this->printLog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print a log
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function printLog(): string
|
||||||
|
{
|
||||||
|
$return = "";
|
||||||
|
foreach ($this->log as $entry) {
|
||||||
|
$return .= $this->printEntry($entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print an entry
|
||||||
|
*
|
||||||
|
* @param EntryInterface|null $entry
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function printEntry(?EntryInterface $entry = null): string
|
||||||
|
{
|
||||||
|
if ($entry === null) {
|
||||||
|
$entry = $this->entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
$return = "";
|
||||||
|
foreach ($entry as $line) {
|
||||||
|
$return .= $this->printLine($line);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print a line
|
||||||
|
*
|
||||||
|
* @param LineInterface $line
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
abstract protected function printLine(LineInterface $line): string;
|
||||||
|
}
|
||||||
37
src/Printer/PrinterInterface.php
Normal file
37
src/Printer/PrinterInterface.php
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Printer;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\EntryInterface;
|
||||||
|
use Aternos\Codex\Log\LogInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface PrinterInterface
|
||||||
|
*
|
||||||
|
* @package Aternos\Codex\Printer
|
||||||
|
*/
|
||||||
|
interface PrinterInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Set the log
|
||||||
|
*
|
||||||
|
* @param LogInterface $log
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLog(LogInterface $log): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the entry
|
||||||
|
*
|
||||||
|
* @param EntryInterface $entry
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setEntry(EntryInterface $entry): static;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print the log
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function print(): string;
|
||||||
|
}
|
||||||
8
test/data/problem.log
Normal file
8
test/data/problem.log
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[01.01.1970 00:00:01] [Log/INFO] This is the first message containing no problem
|
||||||
|
[01.01.1970 00:00:02] [Log/ERROR] I have a problem with ABC
|
||||||
|
[01.01.1970 00:00:03] [Log/INFO] This is a message without any problem
|
||||||
|
[01.01.1970 00:00:04] [Log/ERROR] I have a problem with XYZ
|
||||||
|
[01.01.1970 00:00:05] [Log/ERROR] I have a problem with ABC
|
||||||
|
[01.01.1970 00:00:06] [Log/ERROR] I have a problem with DEF
|
||||||
|
I have a problem with GHI
|
||||||
|
[01.01.1970 00:00:07] [Log/INFO] This log was generated by software v1.2.3
|
||||||
7
test/data/simple.log
Normal file
7
test/data/simple.log
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[01.01.1970 00:00:01] [Log/INFO] This is the first message containing information.
|
||||||
|
[01.01.1970 00:00:02] [Log/DEBUG] This is the second message containing a debug information.
|
||||||
|
[01.01.1970 00:00:03] [Log/WARN] This is the third message containing a warning information.
|
||||||
|
[01.01.1970 00:00:04] [Log/ERROR] This is the third message containing an error information.
|
||||||
|
This line continues the error entry to add even more information.
|
||||||
|
This line is also part of the error entry.
|
||||||
|
[01.01.1970 00:00:05] [Log/INFO] This is the last message of the log.
|
||||||
13
test/src/Analysis/TestInformation.php
Normal file
13
test/src/Analysis/TestInformation.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Src\Analysis;
|
||||||
|
|
||||||
|
use Aternos\Codex\Analysis\Information;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class TestInformation
|
||||||
|
*/
|
||||||
|
class TestInformation extends Information
|
||||||
|
{
|
||||||
|
protected ?string $label = "Label";
|
||||||
|
}
|
||||||
33
test/src/Analysis/TestInsight.php
Normal file
33
test/src/Analysis/TestInsight.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Src\Analysis;
|
||||||
|
|
||||||
|
use Aternos\Codex\Analysis\Insight;
|
||||||
|
use Aternos\Codex\Analysis\InsightInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class TestInsight
|
||||||
|
*/
|
||||||
|
class TestInsight extends Insight
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the insight as human-readable message
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getMessage(): string
|
||||||
|
{
|
||||||
|
return "This is a test insight";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the $insight object is equal with the current object
|
||||||
|
*
|
||||||
|
* @param InsightInterface $insight
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isEqual(InsightInterface $insight): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
43
test/src/Analysis/TestPatternInformation.php
Normal file
43
test/src/Analysis/TestPatternInformation.php
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Src\Analysis;
|
||||||
|
|
||||||
|
use Aternos\Codex\Analysis\Information;
|
||||||
|
use Aternos\Codex\Analysis\PatternInsightInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class TestPatternInformation
|
||||||
|
*/
|
||||||
|
class TestPatternInformation extends Information implements PatternInsightInterface
|
||||||
|
{
|
||||||
|
protected ?string $label = "Software version";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of possible patterns
|
||||||
|
*
|
||||||
|
* The array key of the pattern will be passed to setMatches()
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function getPatterns(): array
|
||||||
|
{
|
||||||
|
return ['/This log was generated by software (v[0-9\.]*)/'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the matches from the pattern
|
||||||
|
*
|
||||||
|
* @param array $matches
|
||||||
|
* @param mixed $patternKey
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setMatches(array $matches, mixed $patternKey): void
|
||||||
|
{
|
||||||
|
$this->value = $matches[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLogContent(): ?string
|
||||||
|
{
|
||||||
|
return parent::getLogContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
70
test/src/Analysis/TestPatternProblem.php
Normal file
70
test/src/Analysis/TestPatternProblem.php
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Src\Analysis;
|
||||||
|
|
||||||
|
use Aternos\Codex\Analysis\InsightInterface;
|
||||||
|
use Aternos\Codex\Analysis\PatternInsightInterface;
|
||||||
|
use Aternos\Codex\Analysis\Problem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class TestPatternProblem
|
||||||
|
*/
|
||||||
|
class TestPatternProblem extends Problem implements PatternInsightInterface
|
||||||
|
{
|
||||||
|
protected ?string $cause = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $cause
|
||||||
|
* @return TestPatternProblem
|
||||||
|
*/
|
||||||
|
public function setCause(string $cause): static
|
||||||
|
{
|
||||||
|
$this->cause = $cause;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of possible patterns
|
||||||
|
*
|
||||||
|
* The array key of the pattern will be passed to setMatches()
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function getPatterns(): array
|
||||||
|
{
|
||||||
|
return ['/I have a problem with (\w+)/'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the matches from the pattern
|
||||||
|
*
|
||||||
|
* @param array $matches
|
||||||
|
* @param mixed $patternKey
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setMatches(array $matches, mixed $patternKey): void
|
||||||
|
{
|
||||||
|
$this->cause = $matches[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the problem as human-readable message
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getMessage(): string
|
||||||
|
{
|
||||||
|
return "There is a problem with " . $this->cause;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the $insight object is equal with the current object
|
||||||
|
*
|
||||||
|
* @param InsightInterface $insight
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isEqual(InsightInterface $insight): bool
|
||||||
|
{
|
||||||
|
return $insight instanceof static && $this->cause === $insight->cause;
|
||||||
|
}
|
||||||
|
}
|
||||||
33
test/src/Analysis/TestProblem.php
Normal file
33
test/src/Analysis/TestProblem.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Src\Analysis;
|
||||||
|
|
||||||
|
use Aternos\Codex\Analysis\InsightInterface;
|
||||||
|
use Aternos\Codex\Analysis\Problem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class TestProblem
|
||||||
|
*/
|
||||||
|
class TestProblem extends Problem
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the problem as human-readable message
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getMessage(): string
|
||||||
|
{
|
||||||
|
return "This is a test problem";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the $insight object is equal with the current object
|
||||||
|
*
|
||||||
|
* @param InsightInterface $insight
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isEqual(InsightInterface $insight): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
test/src/Analysis/TestSolution.php
Normal file
21
test/src/Analysis/TestSolution.php
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Src\Analysis;
|
||||||
|
|
||||||
|
use Aternos\Codex\Analysis\Solution;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class TestSolution
|
||||||
|
*/
|
||||||
|
class TestSolution extends Solution
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the solution as a human-readable message
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getMessage(): string
|
||||||
|
{
|
||||||
|
return "This is a test solution.";
|
||||||
|
}
|
||||||
|
}
|
||||||
24
test/src/Log/TestAlwaysDetectableLog.php
Normal file
24
test/src/Log/TestAlwaysDetectableLog.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Src\Log;
|
||||||
|
|
||||||
|
use Aternos\Codex\Detective\DetectorInterface;
|
||||||
|
use Aternos\Codex\Detective\SinglePatternDetector;
|
||||||
|
use Aternos\Codex\Log\DetectableLogInterface;
|
||||||
|
use Aternos\Codex\Log\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class TestAlwaysDetectableLog
|
||||||
|
*/
|
||||||
|
class TestAlwaysDetectableLog extends Log implements DetectableLogInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get an array of detectors matching DetectorInterface
|
||||||
|
*
|
||||||
|
* @return DetectorInterface[]
|
||||||
|
*/
|
||||||
|
public static function getDetectors(): array
|
||||||
|
{
|
||||||
|
return [(new SinglePatternDetector())->setPattern('/information/')];
|
||||||
|
}
|
||||||
|
}
|
||||||
24
test/src/Log/TestLessDetectableLog.php
Normal file
24
test/src/Log/TestLessDetectableLog.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Src\Log;
|
||||||
|
|
||||||
|
use Aternos\Codex\Detective\DetectorInterface;
|
||||||
|
use Aternos\Codex\Detective\LinePatternDetector;
|
||||||
|
use Aternos\Codex\Log\DetectableLogInterface;
|
||||||
|
use Aternos\Codex\Log\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class TestLessDetectableLog
|
||||||
|
*/
|
||||||
|
class TestLessDetectableLog extends Log implements DetectableLogInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get an array of detectors matching DetectorInterface
|
||||||
|
*
|
||||||
|
* @return DetectorInterface[]
|
||||||
|
*/
|
||||||
|
public static function getDetectors(): array
|
||||||
|
{
|
||||||
|
return [(new LinePatternDetector())->setPattern('/information/')];
|
||||||
|
}
|
||||||
|
}
|
||||||
24
test/src/Log/TestMoreDetectableLog.php
Normal file
24
test/src/Log/TestMoreDetectableLog.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Src\Log;
|
||||||
|
|
||||||
|
use Aternos\Codex\Detective\DetectorInterface;
|
||||||
|
use Aternos\Codex\Detective\LinePatternDetector;
|
||||||
|
use Aternos\Codex\Log\DetectableLogInterface;
|
||||||
|
use Aternos\Codex\Log\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class TestMoreDetectableLog
|
||||||
|
*/
|
||||||
|
class TestMoreDetectableLog extends Log implements DetectableLogInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get an array of detectors matching DetectorInterface
|
||||||
|
*
|
||||||
|
* @return DetectorInterface[]
|
||||||
|
*/
|
||||||
|
public static function getDetectors(): array
|
||||||
|
{
|
||||||
|
return [(new LinePatternDetector())->setPattern('/This/')];
|
||||||
|
}
|
||||||
|
}
|
||||||
24
test/src/Log/TestNeverDetectableLog.php
Normal file
24
test/src/Log/TestNeverDetectableLog.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Src\Log;
|
||||||
|
|
||||||
|
use Aternos\Codex\Detective\DetectorInterface;
|
||||||
|
use Aternos\Codex\Detective\SinglePatternDetector;
|
||||||
|
use Aternos\Codex\Log\DetectableLogInterface;
|
||||||
|
use Aternos\Codex\Log\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class TestNeverDetectableLog
|
||||||
|
*/
|
||||||
|
class TestNeverDetectableLog extends Log implements DetectableLogInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get an array of detectors matching DetectorInterface
|
||||||
|
*
|
||||||
|
* @return DetectorInterface[]
|
||||||
|
*/
|
||||||
|
public static function getDetectors(): array
|
||||||
|
{
|
||||||
|
return [(new SinglePatternDetector())->setPattern('/missing/')];
|
||||||
|
}
|
||||||
|
}
|
||||||
40
test/src/Log/TestPatternLog.php
Normal file
40
test/src/Log/TestPatternLog.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Src\Log;
|
||||||
|
|
||||||
|
use Aternos\Codex\Analyser\PatternAnalyser;
|
||||||
|
use Aternos\Codex\Log\AnalysableLog;
|
||||||
|
use Aternos\Codex\Parser\PatternParser;
|
||||||
|
use Aternos\Codex\Test\Src\Analysis\TestPatternInformation;
|
||||||
|
use Aternos\Codex\Test\Src\Analysis\TestPatternProblem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class TestLog
|
||||||
|
*/
|
||||||
|
class TestPatternLog extends AnalysableLog
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the default parser
|
||||||
|
*
|
||||||
|
* @return PatternParser
|
||||||
|
*/
|
||||||
|
public static function getDefaultParser(): PatternParser
|
||||||
|
{
|
||||||
|
return (new PatternParser())
|
||||||
|
->setPattern('/(\[([^\]]+)\] \[[^\/]+\/([^\]]+)\]).*/')
|
||||||
|
->setMatches([PatternParser::PREFIX, PatternParser::TIME, PatternParser::LEVEL])
|
||||||
|
->setTimeFormat('d.m.Y H:i:s');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the default analyser
|
||||||
|
*
|
||||||
|
* @return PatternAnalyser
|
||||||
|
*/
|
||||||
|
public static function getDefaultAnalyser(): PatternAnalyser
|
||||||
|
{
|
||||||
|
return (new PatternAnalyser())
|
||||||
|
->addPossibleInsightClass(TestPatternProblem::class)
|
||||||
|
->addPossibleInsightClass(TestPatternInformation::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
test/src/Printer/TestModification.php
Normal file
22
test/src/Printer/TestModification.php
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Src\Printer;
|
||||||
|
|
||||||
|
use Aternos\Codex\Printer\Modification;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class TestModification
|
||||||
|
*/
|
||||||
|
class TestModification extends Modification
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Modify the given string and return it
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function modify(string $text): string
|
||||||
|
{
|
||||||
|
return str_replace("foo", "bar", $text);
|
||||||
|
}
|
||||||
|
}
|
||||||
177
test/tests/Analyser/PatternAnalyserTest.php
Normal file
177
test/tests/Analyser/PatternAnalyserTest.php
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Tests\Analyser;
|
||||||
|
|
||||||
|
use Aternos\Codex\Analyser\PatternAnalyser;
|
||||||
|
use Aternos\Codex\Analysis\Analysis;
|
||||||
|
use Aternos\Codex\Analysis\PatternInsightInterface;
|
||||||
|
use Aternos\Codex\Log\Entry;
|
||||||
|
use Aternos\Codex\Log\File\PathLogFile;
|
||||||
|
use Aternos\Codex\Log\Level;
|
||||||
|
use Aternos\Codex\Log\Line;
|
||||||
|
use Aternos\Codex\Test\Src\Analysis\TestPatternInformation;
|
||||||
|
use Aternos\Codex\Test\Src\Analysis\TestPatternProblem;
|
||||||
|
use Aternos\Codex\Test\Src\Analysis\TestSolution;
|
||||||
|
use Aternos\Codex\Test\Src\Log\TestPatternLog;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use ReflectionClass;
|
||||||
|
|
||||||
|
class PatternAnalyserTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return Analysis
|
||||||
|
*/
|
||||||
|
protected function getExpectedAnalysis(): Analysis
|
||||||
|
{
|
||||||
|
return (new Analysis())
|
||||||
|
->addInsight((new TestPatternProblem())
|
||||||
|
->setCause("ABC")
|
||||||
|
->increaseCounter()
|
||||||
|
->setEntry((new Entry())->setTime(2)->setLevel(Level::ERROR)->setPrefix("[01.01.1970 00:00:02] [Log/ERROR]")
|
||||||
|
->addLine(new Line(2, "[01.01.1970 00:00:02] [Log/ERROR] I have a problem with ABC"))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
->addInsight((new TestPatternProblem())
|
||||||
|
->setCause("XYZ")
|
||||||
|
->setEntry((new Entry())->setTime(4)->setLevel(Level::ERROR)->setPrefix("[01.01.1970 00:00:04] [Log/ERROR]")
|
||||||
|
->addLine(new Line(4, "[01.01.1970 00:00:04] [Log/ERROR] I have a problem with XYZ"))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
->addInsight((new TestPatternProblem())
|
||||||
|
->setCause("DEF")
|
||||||
|
->setEntry((new Entry())->setTime(6)->setLevel(Level::ERROR)->setPrefix("[01.01.1970 00:00:06] [Log/ERROR]")
|
||||||
|
->addLine(new Line(6, "[01.01.1970 00:00:06] [Log/ERROR] I have a problem with DEF"))
|
||||||
|
->addLine(new Line(7, "I have a problem with GHI"))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
->addInsight((new TestPatternProblem())
|
||||||
|
->setCause("GHI")
|
||||||
|
->setEntry((new Entry())->setTime(6)->setLevel(Level::ERROR)->setPrefix("[01.01.1970 00:00:06] [Log/ERROR]")
|
||||||
|
->addLine(new Line(6, "[01.01.1970 00:00:06] [Log/ERROR] I have a problem with DEF"))
|
||||||
|
->addLine(new Line(7, "I have a problem with GHI"))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
->addInsight((new TestPatternInformation())
|
||||||
|
->setValue("v1.2.3")
|
||||||
|
->setEntry((new Entry())->setTime(7)->setLevel(Level::INFO)->setPrefix("[01.01.1970 00:00:07] [Log/INFO]")
|
||||||
|
->addLine(new Line(8, "[01.01.1970 00:00:07] [Log/INFO] This log was generated by software v1.2.3"))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAnalyse(): void
|
||||||
|
{
|
||||||
|
$logFile = new PathLogFile(__DIR__ . '/../../data/problem.log');
|
||||||
|
$log = (new TestPatternLog())->setLogFile($logFile);
|
||||||
|
$log->parse();
|
||||||
|
|
||||||
|
$analysis = $log->analyse();
|
||||||
|
$this->assertJsonStringEqualsJsonString(json_encode($this->getExpectedAnalysis()->getInsights()), json_encode($analysis->getInsights()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAnalyseWithPossibleInsightClasses(): void
|
||||||
|
{
|
||||||
|
$logFile = new PathLogFile(__DIR__ . '/../../data/problem.log');
|
||||||
|
$log = (new TestPatternLog())->setLogFile($logFile);
|
||||||
|
$log->parse();
|
||||||
|
|
||||||
|
$analyser = (new PatternAnalyser())
|
||||||
|
->setPossibleInsightClasses([
|
||||||
|
TestPatternInformation::class,
|
||||||
|
TestPatternProblem::class
|
||||||
|
]);
|
||||||
|
|
||||||
|
$analysis = $log->analyse($analyser);
|
||||||
|
$this->assertJsonStringEqualsJsonString(json_encode($this->getExpectedAnalysis()->getInsights()), json_encode($analysis->getInsights()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddPossibleInsightClassThrowsExceptionIfPossibleInsightClassDoesNotImplementPatternInsightInterface(): void
|
||||||
|
{
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionMessage("Class " . TestSolution::class . " does not implement " . PatternInsightInterface::class . ".");
|
||||||
|
(new PatternAnalyser())->addPossibleInsightClass(TestSolution::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRemovePossibleInsightClass(): void
|
||||||
|
{
|
||||||
|
$analyser = (new PatternAnalyser())
|
||||||
|
->setPossibleInsightClasses([
|
||||||
|
TestPatternInformation::class,
|
||||||
|
TestPatternProblem::class
|
||||||
|
]);
|
||||||
|
|
||||||
|
$reflector = new ReflectionClass(PatternAnalyser::class);
|
||||||
|
$possibleInsightClassesProperty = $reflector->getProperty('possibleInsightClasses');
|
||||||
|
|
||||||
|
$this->assertEquals([TestPatternInformation::class, TestPatternProblem::class], $possibleInsightClassesProperty->getValue($analyser));
|
||||||
|
|
||||||
|
$analyser->removePossibleInsightClass(TestPatternProblem::class);
|
||||||
|
|
||||||
|
$this->assertEquals([TestPatternInformation::class], $possibleInsightClassesProperty->getValue($analyser));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRemovePossibleInsightClassThrowsExceptionIfPossibleInsightClassIsNotAdded(): void
|
||||||
|
{
|
||||||
|
// Set TestPatternProblem class
|
||||||
|
$analyser = (new PatternAnalyser())
|
||||||
|
->setPossibleInsightClasses([
|
||||||
|
TestPatternProblem::class
|
||||||
|
]);
|
||||||
|
|
||||||
|
$reflector = new ReflectionClass(PatternAnalyser::class);
|
||||||
|
$possibleInsightClassesProperty = $reflector->getProperty('possibleInsightClasses');
|
||||||
|
|
||||||
|
$this->assertEquals([TestPatternProblem::class], $possibleInsightClassesProperty->getValue($analyser));
|
||||||
|
|
||||||
|
// Remove TestPatternInformation class -> not found
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionMessage("Class " . TestPatternInformation::class . " not found in possible insight classes.");
|
||||||
|
$analyser->removePossibleInsightClass(TestPatternInformation::class);
|
||||||
|
|
||||||
|
$this->assertEquals([TestPatternInformation::class], $possibleInsightClassesProperty->getValue($analyser));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOverridePossibleInsightClass(): void
|
||||||
|
{
|
||||||
|
$analyser = (new PatternAnalyser())
|
||||||
|
->setPossibleInsightClasses([
|
||||||
|
TestPatternProblem::class
|
||||||
|
]);
|
||||||
|
|
||||||
|
$reflector = new ReflectionClass(PatternAnalyser::class);
|
||||||
|
$possibleInsightClassesProperty = $reflector->getProperty('possibleInsightClasses');
|
||||||
|
|
||||||
|
$this->assertEquals([TestPatternProblem::class], $possibleInsightClassesProperty->getValue($analyser));
|
||||||
|
|
||||||
|
$childInsightClass = new class extends TestPatternProblem {
|
||||||
|
// Is empty child class
|
||||||
|
};
|
||||||
|
|
||||||
|
$analyser->overridePossibleInsightClass(TestPatternProblem::class, get_class($childInsightClass));
|
||||||
|
|
||||||
|
$this->assertEquals([get_class($childInsightClass)], $possibleInsightClassesProperty->getValue($analyser));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOverridePossibleInsightClassThrowsExceptionIfClassDoesNotExtendParent(): void
|
||||||
|
{
|
||||||
|
$analyser = (new PatternAnalyser())
|
||||||
|
->setPossibleInsightClasses([
|
||||||
|
TestPatternProblem::class
|
||||||
|
]);
|
||||||
|
|
||||||
|
$reflector = new ReflectionClass(PatternAnalyser::class);
|
||||||
|
$possibleInsightClassesProperty = $reflector->getProperty('possibleInsightClasses');
|
||||||
|
|
||||||
|
$this->assertEquals([TestPatternProblem::class], $possibleInsightClassesProperty->getValue($analyser));
|
||||||
|
|
||||||
|
$childInsightClass = new class {
|
||||||
|
// Is empty and not a child class
|
||||||
|
};
|
||||||
|
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionMessage("Class " . get_class($childInsightClass) . " does not extend " . TestPatternProblem::class . ".");
|
||||||
|
$analyser->overridePossibleInsightClass(TestPatternProblem::class, get_class($childInsightClass));
|
||||||
|
|
||||||
|
$this->assertEquals([TestPatternProblem::class], $possibleInsightClassesProperty->getValue($analyser));
|
||||||
|
}
|
||||||
|
}
|
||||||
157
test/tests/Analysis/AnalysisTest.php
Normal file
157
test/tests/Analysis/AnalysisTest.php
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Tests\Analysis;
|
||||||
|
|
||||||
|
use Aternos\Codex\Analysis\Analysis;
|
||||||
|
use Aternos\Codex\Test\Src\Analysis\TestInformation;
|
||||||
|
use Aternos\Codex\Test\Src\Analysis\TestInsight;
|
||||||
|
use Aternos\Codex\Test\Src\Analysis\TestPatternProblem;
|
||||||
|
use Aternos\Codex\Test\Src\Analysis\TestProblem;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class AnalysisTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testSetGetInsights(): void
|
||||||
|
{
|
||||||
|
$analysis = new Analysis();
|
||||||
|
$insight = new TestInsight();
|
||||||
|
$this->assertSame($analysis, $analysis->setInsights([$insight]));
|
||||||
|
$this->assertSame([$insight], $analysis->getInsights());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddInsight(): void
|
||||||
|
{
|
||||||
|
$analysis = new Analysis();
|
||||||
|
$insight = new TestInsight();
|
||||||
|
$this->assertSame($analysis, $analysis->addInsight($insight));
|
||||||
|
$this->assertSame([$insight], $analysis->getInsights());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetProblems(): void
|
||||||
|
{
|
||||||
|
$analysis = new Analysis();
|
||||||
|
$problem = new TestProblem();
|
||||||
|
$information = new TestInformation();
|
||||||
|
|
||||||
|
$analysis->addInsight($problem);
|
||||||
|
$analysis->addInsight($information);
|
||||||
|
|
||||||
|
$this->assertEquals([$problem], $analysis->getProblems());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetInformation(): void
|
||||||
|
{
|
||||||
|
$analysis = new Analysis();
|
||||||
|
$problem = new TestProblem();
|
||||||
|
$information = new TestInformation();
|
||||||
|
|
||||||
|
$analysis->addInsight($problem);
|
||||||
|
$analysis->addInsight($information);
|
||||||
|
|
||||||
|
$this->assertEquals([$information], $analysis->getInformation());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testKey(): void
|
||||||
|
{
|
||||||
|
$analysis = new Analysis();
|
||||||
|
$problem = new TestProblem();
|
||||||
|
$information = new TestInformation();
|
||||||
|
|
||||||
|
$analysis->addInsight($problem);
|
||||||
|
$this->assertEquals(0, $analysis->key());
|
||||||
|
$analysis->addInsight($information);
|
||||||
|
$this->assertEquals(1, $analysis->key());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function testCount(): void
|
||||||
|
{
|
||||||
|
$analysis = new Analysis();
|
||||||
|
$problem = new TestProblem();
|
||||||
|
$information = new TestInformation();
|
||||||
|
|
||||||
|
$this->assertEquals(0, $analysis->count());
|
||||||
|
$analysis->addInsight($problem);
|
||||||
|
$this->assertEquals(1, $analysis->count());
|
||||||
|
$analysis->addInsight($information);
|
||||||
|
$this->assertEquals(2, $analysis->count());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddingTheSameInsightIncreasesInternalCounter(): void
|
||||||
|
{
|
||||||
|
// Adding the same insight to an analysis does not add it to the insights, and therefore it
|
||||||
|
// does not increase the counter of the analysis, but the internal counter of the insight.
|
||||||
|
// See Analysis->addInsight()
|
||||||
|
|
||||||
|
$analysis = new Analysis();
|
||||||
|
$problem = new TestPatternProblem();
|
||||||
|
$problem2 = new TestPatternProblem();
|
||||||
|
|
||||||
|
$analysis->addInsight($problem);
|
||||||
|
$this->assertEquals(1, $analysis->count());
|
||||||
|
$this->assertEquals(1, $problem->getCounterValue());
|
||||||
|
|
||||||
|
$analysis->addInsight($problem2);
|
||||||
|
$this->assertEquals(1, $analysis->count());
|
||||||
|
$this->assertEquals(2, $problem->getCounterValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOffsetExists(): void
|
||||||
|
{
|
||||||
|
$analysis = new Analysis();
|
||||||
|
$information = new TestInformation();
|
||||||
|
|
||||||
|
$this->assertArrayNotHasKey(0, $analysis);
|
||||||
|
$this->assertEquals(0, $analysis->count());
|
||||||
|
$analysis->addInsight($information);
|
||||||
|
$this->assertArrayHasKey(0, $analysis);
|
||||||
|
$this->assertEquals($information, $analysis[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOffsetGet(): void
|
||||||
|
{
|
||||||
|
$analysis = new Analysis();
|
||||||
|
$information = new TestInformation();
|
||||||
|
$analysis->addInsight($information);
|
||||||
|
|
||||||
|
// Exists
|
||||||
|
$this->assertEquals($information, $analysis[0]);
|
||||||
|
|
||||||
|
// Does not exist -> "undefined array key" error
|
||||||
|
$this->assertArrayNotHasKey(1, $analysis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOffsetSet(): void
|
||||||
|
{
|
||||||
|
$analysis = new Analysis();
|
||||||
|
$information = new TestInformation();
|
||||||
|
|
||||||
|
$this->assertArrayNotHasKey(0, $analysis);
|
||||||
|
$this->assertEquals(0, $analysis->count());
|
||||||
|
$analysis->addInsight($information);
|
||||||
|
$this->assertArrayHasKey(0, $analysis);
|
||||||
|
$this->assertEquals($information, $analysis[0]);
|
||||||
|
|
||||||
|
// Overwrite $information on $analysis[0] using the offsetSet
|
||||||
|
$problem = new TestProblem();
|
||||||
|
$analysis[0] = $problem;
|
||||||
|
$this->assertEquals($problem, $analysis[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOffsetUnset(): void
|
||||||
|
{
|
||||||
|
$analysis = new Analysis();
|
||||||
|
$information = new TestInformation();
|
||||||
|
|
||||||
|
$this->assertArrayNotHasKey(0, $analysis);
|
||||||
|
$this->assertEquals(0, $analysis->count());
|
||||||
|
$analysis->addInsight($information);
|
||||||
|
$this->assertArrayHasKey(0, $analysis);
|
||||||
|
$this->assertEquals($information, $analysis[0]);
|
||||||
|
|
||||||
|
// Unset $information on $analysis[0] using the offsetUnset
|
||||||
|
unset($analysis[0]);
|
||||||
|
$this->assertArrayNotHasKey(0, $analysis);
|
||||||
|
$this->assertArrayNotHasKey(1, $analysis);
|
||||||
|
}
|
||||||
|
}
|
||||||
61
test/tests/Analysis/InformationTest.php
Normal file
61
test/tests/Analysis/InformationTest.php
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Tests\Analysis;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\File\PathLogFile;
|
||||||
|
use Aternos\Codex\Test\Src\Analysis\TestInformation;
|
||||||
|
use Aternos\Codex\Test\Src\Analysis\TestPatternInformation;
|
||||||
|
use Aternos\Codex\Test\Src\Log\TestPatternLog;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class InformationTest extends TestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
public function testSetGetValue(): void
|
||||||
|
{
|
||||||
|
$value = uniqid();
|
||||||
|
$information = new TestInformation();
|
||||||
|
$information->setValue($value);
|
||||||
|
$this->assertEquals($value, $information->getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetLabel(): void
|
||||||
|
{
|
||||||
|
$this->assertEquals("Label", (new TestInformation())->getLabel());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetMessage(): void
|
||||||
|
{
|
||||||
|
$value = uniqid();
|
||||||
|
$information = new TestInformation();
|
||||||
|
$information->setValue($value);
|
||||||
|
$this->assertEquals("Label: " . $value, $information->getMessage());
|
||||||
|
$this->assertEquals("Label: " . $value, (string)$information);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsEqual(): void
|
||||||
|
{
|
||||||
|
$value = uniqid();
|
||||||
|
$informationA = new TestInformation();
|
||||||
|
$informationA->setValue($value);
|
||||||
|
$informationB = new TestInformation();
|
||||||
|
$informationB->setValue($value);
|
||||||
|
|
||||||
|
$this->assertTrue($informationA->isEqual($informationB));
|
||||||
|
$this->assertTrue($informationA->isEqual($informationA));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetLogContent(): void
|
||||||
|
{
|
||||||
|
$logFile = new PathLogFile(__DIR__ . '/../../data/problem.log');
|
||||||
|
$log = (new TestPatternLog())->setLogFile($logFile);
|
||||||
|
$log->parse();
|
||||||
|
|
||||||
|
$analysis = $log->analyse();
|
||||||
|
foreach ($analysis->getInformation() as $information) {
|
||||||
|
/** @var TestPatternInformation $information */
|
||||||
|
$this->assertNotNull($information->getLogContent());
|
||||||
|
$this->assertEquals($logFile->getContent(), $information->getLogContent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
113
test/tests/Analysis/ProblemTest.php
Normal file
113
test/tests/Analysis/ProblemTest.php
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Tests\Analysis;
|
||||||
|
|
||||||
|
use Aternos\Codex\Test\Src\Analysis\TestProblem;
|
||||||
|
use Aternos\Codex\Test\Src\Analysis\TestSolution;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class ProblemTest extends TestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
public function testSetGetSolutions(): void
|
||||||
|
{
|
||||||
|
$problem = new TestProblem();
|
||||||
|
$solution = new TestSolution();
|
||||||
|
$this->assertSame($problem, $problem->setSolutions([$solution]));
|
||||||
|
$this->assertEquals([$solution], $problem->getSolutions());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddSolutions(): void
|
||||||
|
{
|
||||||
|
$problem = new TestProblem();
|
||||||
|
$solution = new TestSolution();
|
||||||
|
$this->assertSame($problem, $problem->addSolution($solution));
|
||||||
|
$this->assertEquals([$solution], $problem->getSolutions());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testKey(): void
|
||||||
|
{
|
||||||
|
$problem = new TestProblem();
|
||||||
|
$solution = new TestSolution();
|
||||||
|
|
||||||
|
$problem->addSolution($solution);
|
||||||
|
/** @noinspection PhpStatementHasEmptyBodyInspection */
|
||||||
|
foreach ($problem as $ignored) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
$this->assertEquals(1, $problem->key());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function testCount(): void
|
||||||
|
{
|
||||||
|
$problem = new TestProblem();
|
||||||
|
$solution1 = new TestSolution();
|
||||||
|
$solution2 = new TestSolution();
|
||||||
|
|
||||||
|
$this->assertEquals(0, $problem->count());
|
||||||
|
$problem->addSolution($solution1);
|
||||||
|
$this->assertEquals(1, $problem->count());
|
||||||
|
$problem->addSolution($solution2);
|
||||||
|
$this->assertEquals(2, $problem->count());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOffsetExists(): void
|
||||||
|
{
|
||||||
|
$problem = new TestProblem();
|
||||||
|
$solution = new TestSolution();
|
||||||
|
|
||||||
|
$this->assertArrayNotHasKey(0, $problem);
|
||||||
|
$this->assertEquals(0, $problem->count());
|
||||||
|
$problem->addSolution($solution);
|
||||||
|
$this->assertArrayHasKey(0, $problem);
|
||||||
|
$this->assertEquals($solution, $problem[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOffsetGet(): void
|
||||||
|
{
|
||||||
|
$problem = new TestProblem();
|
||||||
|
$solution = new TestSolution();
|
||||||
|
$problem->addSolution($solution);
|
||||||
|
|
||||||
|
// Exists
|
||||||
|
$this->assertEquals($solution, $problem[0]);
|
||||||
|
|
||||||
|
// Does not exist -> "undefined array key" error
|
||||||
|
$this->assertArrayNotHasKey(1, $problem);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOffsetSet(): void
|
||||||
|
{
|
||||||
|
$problem = new TestProblem();
|
||||||
|
$solution1 = new TestSolution();
|
||||||
|
|
||||||
|
$this->assertArrayNotHasKey(0, $problem);
|
||||||
|
$this->assertEquals(0, $problem->count());
|
||||||
|
$problem->addSolution($solution1);
|
||||||
|
$this->assertArrayHasKey(0, $problem);
|
||||||
|
$this->assertEquals($solution1, $problem[0]);
|
||||||
|
|
||||||
|
// Overwrite $solution1 on $problem[0] using the offsetSet
|
||||||
|
$TestSolution2 = new TestSolution();
|
||||||
|
$problem[0] = $TestSolution2;
|
||||||
|
$this->assertEquals($TestSolution2, $problem[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOffsetUnset(): void
|
||||||
|
{
|
||||||
|
$problem = new TestProblem();
|
||||||
|
$solution = new TestSolution();
|
||||||
|
|
||||||
|
$this->assertArrayNotHasKey(0, $problem);
|
||||||
|
$this->assertEquals(0, $problem->count());
|
||||||
|
$problem->addSolution($solution);
|
||||||
|
$this->assertArrayHasKey(0, $problem);
|
||||||
|
$this->assertEquals($solution, $problem[0]);
|
||||||
|
|
||||||
|
// Unset $solution on $problem[0] using the offsetUnset
|
||||||
|
unset($problem[0]);
|
||||||
|
$this->assertArrayNotHasKey(0, $problem);
|
||||||
|
$this->assertArrayNotHasKey(1, $problem);
|
||||||
|
}
|
||||||
|
}
|
||||||
122
test/tests/Detective/DetectiveTest.php
Normal file
122
test/tests/Detective/DetectiveTest.php
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Tests\Detective;
|
||||||
|
|
||||||
|
use Aternos\Codex\Detective\Detective;
|
||||||
|
use Aternos\Codex\Detective\DetectorInterface;
|
||||||
|
use Aternos\Codex\Log\DetectableLogInterface;
|
||||||
|
use Aternos\Codex\Log\Log;
|
||||||
|
use Aternos\Codex\Test\Src\Analysis\TestSolution;
|
||||||
|
use Aternos\Codex\Test\Src\Log\TestAlwaysDetectableLog;
|
||||||
|
use Aternos\Codex\Test\Src\Log\TestLessDetectableLog;
|
||||||
|
use Aternos\Codex\Test\Src\Log\TestMoreDetectableLog;
|
||||||
|
use Aternos\Codex\Test\Src\Log\TestNeverDetectableLog;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class DetectiveTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $possibleLogClasses
|
||||||
|
* @return Detective
|
||||||
|
*/
|
||||||
|
protected function getDetective(array $possibleLogClasses): Detective
|
||||||
|
{
|
||||||
|
$detective = new Detective();
|
||||||
|
$detective->setLogFile(new \Aternos\Codex\Log\File\PathLogFile(__DIR__ . '/../../data/simple.log'));
|
||||||
|
$detective->setPossibleLogClasses($possibleLogClasses);
|
||||||
|
return $detective;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDetect(): void
|
||||||
|
{
|
||||||
|
$this->assertEquals(TestAlwaysDetectableLog::class,
|
||||||
|
get_class($this->getDetective([
|
||||||
|
TestAlwaysDetectableLog::class,
|
||||||
|
TestLessDetectableLog::class,
|
||||||
|
TestMoreDetectableLog::class,
|
||||||
|
TestNeverDetectableLog::class])->detect()));
|
||||||
|
|
||||||
|
$this->assertEquals(TestMoreDetectableLog::class,
|
||||||
|
get_class($this->getDetective([
|
||||||
|
TestLessDetectableLog::class,
|
||||||
|
TestMoreDetectableLog::class,
|
||||||
|
TestNeverDetectableLog::class])->detect()));
|
||||||
|
|
||||||
|
$this->assertEquals(TestLessDetectableLog::class,
|
||||||
|
get_class($this->getDetective([
|
||||||
|
TestLessDetectableLog::class,
|
||||||
|
TestNeverDetectableLog::class])->detect()));
|
||||||
|
|
||||||
|
$this->assertEquals(Log::class,
|
||||||
|
get_class($this->getDetective([
|
||||||
|
TestNeverDetectableLog::class])->detect()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddPossibleLogClassThrowsExceptionIfPossibleClassDoesNotImplementDetectableLogInterface(): void
|
||||||
|
{
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionMessage("Class " . TestSolution::class . " does not implement " . DetectableLogInterface::class . ".");
|
||||||
|
(new Detective())->addPossibleLogClass(TestSolution::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDetectThrowsExceptionIfDetectorClassDoesNotImplementDetectorInterface(): void
|
||||||
|
{
|
||||||
|
$invalidDetectorClass = new class {
|
||||||
|
// Is empty and not a child class of DetectorInterface
|
||||||
|
};
|
||||||
|
|
||||||
|
$customLogClass = new class() extends Log implements DetectableLogInterface {
|
||||||
|
private static array $detectors = [];
|
||||||
|
|
||||||
|
public static function setDetectors($detectors): void
|
||||||
|
{
|
||||||
|
self::$detectors = $detectors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getDetectors(): array
|
||||||
|
{
|
||||||
|
return self::$detectors;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$customLogClass::setDetectors([$invalidDetectorClass]);
|
||||||
|
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionMessage("Class " . get_class($invalidDetectorClass) . " does not implement " . DetectorInterface::class . ".");
|
||||||
|
$this->getDetective([get_class($customLogClass)])->detect();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetPossibleLogClasses(): void
|
||||||
|
{
|
||||||
|
$possibleLogClasses = [
|
||||||
|
TestAlwaysDetectableLog::class,
|
||||||
|
TestLessDetectableLog::class,
|
||||||
|
TestMoreDetectableLog::class,
|
||||||
|
TestNeverDetectableLog::class
|
||||||
|
];
|
||||||
|
|
||||||
|
$detective = $this->getDetective($possibleLogClasses);
|
||||||
|
|
||||||
|
$this->assertEquals($possibleLogClasses, $detective->getPossibleLogClasses());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddDetective(): void
|
||||||
|
{
|
||||||
|
$possibleLogClasses1 = [
|
||||||
|
TestAlwaysDetectableLog::class,
|
||||||
|
TestLessDetectableLog::class
|
||||||
|
];
|
||||||
|
|
||||||
|
$possibleLogClasses2 = [
|
||||||
|
TestMoreDetectableLog::class,
|
||||||
|
TestNeverDetectableLog::class
|
||||||
|
];
|
||||||
|
|
||||||
|
$detective1 = $this->getDetective($possibleLogClasses1);
|
||||||
|
$detective2 = $this->getDetective($possibleLogClasses2);
|
||||||
|
|
||||||
|
$detective1->addDetective($detective2);
|
||||||
|
|
||||||
|
$this->assertEquals(array_merge($possibleLogClasses1, $possibleLogClasses2), $detective1->getPossibleLogClasses());
|
||||||
|
}
|
||||||
|
}
|
||||||
30
test/tests/Detective/LinePatternDetectorTest.php
Normal file
30
test/tests/Detective/LinePatternDetectorTest.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Tests\Detective;
|
||||||
|
|
||||||
|
use Aternos\Codex\Detective\LinePatternDetector;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class LinePatternDetectorTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testDetect(): void
|
||||||
|
{
|
||||||
|
$this->assertEquals(5 / 7, (new LinePatternDetector())
|
||||||
|
->setLogFile(new \Aternos\Codex\Log\File\PathLogFile(__DIR__ . '/../../data/simple.log'))
|
||||||
|
->setPattern('/information/')
|
||||||
|
->detect()
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertFalse((new LinePatternDetector())
|
||||||
|
->setLogFile(new \Aternos\Codex\Log\File\PathLogFile(__DIR__ . '/../../data/simple.log'))
|
||||||
|
->setPattern('/missing/')
|
||||||
|
->detect()
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(1, (new LinePatternDetector())
|
||||||
|
->setLogFile(new \Aternos\Codex\Log\File\PathLogFile(__DIR__ . '/../../data/simple.log'))
|
||||||
|
->setPattern('/This/')
|
||||||
|
->detect()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
40
test/tests/Detective/MultiPatternDetectorTest.php
Normal file
40
test/tests/Detective/MultiPatternDetectorTest.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Tests\Detective;
|
||||||
|
|
||||||
|
use Aternos\Codex\Detective\MultiPatternDetector;
|
||||||
|
use Aternos\Codex\Log\File\StringLogFile;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class MultiPatternDetectorTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testDetectSinglePattern(): void
|
||||||
|
{
|
||||||
|
$this->assertTrue((new MultiPatternDetector())
|
||||||
|
->setLogFile(new StringLogFile("You can detect this."))
|
||||||
|
->addPattern('/detect/')
|
||||||
|
->detect()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDetectMultiplePatterns(): void
|
||||||
|
{
|
||||||
|
$this->assertTrue((new MultiPatternDetector())
|
||||||
|
->setLogFile(new StringLogFile("You can detect this and this."))
|
||||||
|
->addPattern('/detect/')
|
||||||
|
->addPattern('/and this/')
|
||||||
|
->detect()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNotDetectMissingFromMultiplePatterns(): void
|
||||||
|
{
|
||||||
|
$this->assertFalse((new MultiPatternDetector())
|
||||||
|
->setLogFile(new StringLogFile("You can detect this and this."))
|
||||||
|
->addPattern('/detect/')
|
||||||
|
->addPattern('/and this/')
|
||||||
|
->addPattern('/but not this/')
|
||||||
|
->detect()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
test/tests/Detective/SinglePatternDetectorTest.php
Normal file
25
test/tests/Detective/SinglePatternDetectorTest.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Tests\Detective;
|
||||||
|
|
||||||
|
use Aternos\Codex\Detective\SinglePatternDetector;
|
||||||
|
use Aternos\Codex\Log\File\StringLogFile;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class SinglePatternDetectorTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testDetect(): void
|
||||||
|
{
|
||||||
|
$this->assertTrue((new SinglePatternDetector())
|
||||||
|
->setLogFile(new StringLogFile("You can detect this."))
|
||||||
|
->setPattern('/detect/')
|
||||||
|
->detect()
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertFalse((new SinglePatternDetector())
|
||||||
|
->setLogFile(new StringLogFile("You cannot detect this."))
|
||||||
|
->setPattern('/missing/')
|
||||||
|
->detect()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
40
test/tests/Detective/WeightedSinglePatternDetectorTest.php
Normal file
40
test/tests/Detective/WeightedSinglePatternDetectorTest.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Tests\Detective;
|
||||||
|
|
||||||
|
use Aternos\Codex\Detective\WeightedSinglePatternDetector;
|
||||||
|
use Aternos\Codex\Log\File\StringLogFile;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class WeightedSinglePatternDetectorTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testDetect(): void
|
||||||
|
{
|
||||||
|
$this->assertEquals(1, (new WeightedSinglePatternDetector())
|
||||||
|
->setLogFile(new \Aternos\Codex\Log\File\PathLogFile(__DIR__ . '/../../data/simple.log'))
|
||||||
|
->setPattern('/This/')
|
||||||
|
->setWeight(1)
|
||||||
|
->detect()
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(0.5, (new WeightedSinglePatternDetector())
|
||||||
|
->setLogFile(new \Aternos\Codex\Log\File\PathLogFile(__DIR__ . '/../../data/simple.log'))
|
||||||
|
->setPattern('/This/')
|
||||||
|
->setWeight(0.5)
|
||||||
|
->detect()
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(0, (new WeightedSinglePatternDetector())
|
||||||
|
->setLogFile(new \Aternos\Codex\Log\File\PathLogFile(__DIR__ . '/../../data/simple.log'))
|
||||||
|
->setPattern('/This/')
|
||||||
|
->setWeight(0)
|
||||||
|
->detect()
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertFalse((new WeightedSinglePatternDetector())
|
||||||
|
->setLogFile(new StringLogFile("You cannot detect this."))
|
||||||
|
->setPattern('/missing/')
|
||||||
|
->detect()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
138
test/tests/Log/EntryTest.php
Normal file
138
test/tests/Log/EntryTest.php
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Tests\Log;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\Entry;
|
||||||
|
use Aternos\Codex\Log\Level;
|
||||||
|
use Aternos\Codex\Log\Line;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class EntryTest extends TestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
public function testAddLine(): void
|
||||||
|
{
|
||||||
|
$entry = new Entry();
|
||||||
|
$line = new Line(1, uniqid());
|
||||||
|
$this->assertSame($entry, $entry->addLine($line));
|
||||||
|
$this->assertEquals([$line], $entry->getLines());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetGetLines(): void
|
||||||
|
{
|
||||||
|
$entry = new Entry();
|
||||||
|
$line = new Line(1, uniqid());
|
||||||
|
$this->assertSame($entry, $entry->setLines([$line]));
|
||||||
|
$this->assertEquals([$line], $entry->getLines());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetGetLevel(): void
|
||||||
|
{
|
||||||
|
$entry = new Entry();
|
||||||
|
$level = Level::CRITICAL;
|
||||||
|
$this->assertSame($entry, $entry->setLevel($level));
|
||||||
|
$this->assertEquals($level, $entry->getLevel());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetGetTime(): void
|
||||||
|
{
|
||||||
|
$entry = new Entry();
|
||||||
|
$time = time();
|
||||||
|
$this->assertSame($entry, $entry->setTime($time));
|
||||||
|
$this->assertEquals($time, $entry->getTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetGetPrefix(): void
|
||||||
|
{
|
||||||
|
$entry = new Entry();
|
||||||
|
$prefix = uniqid();
|
||||||
|
$this->assertSame($entry, $entry->setPrefix($prefix));
|
||||||
|
$this->assertEquals($prefix, $entry->getPrefix());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testKey(): void
|
||||||
|
{
|
||||||
|
$entry = new Entry();
|
||||||
|
$line = new Line(1, uniqid());
|
||||||
|
|
||||||
|
$entry->addLine($line);
|
||||||
|
/** @noinspection PhpStatementHasEmptyBodyInspection */
|
||||||
|
foreach ($entry as $ignored) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
$this->assertEquals(1, $entry->key());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function testCount(): void
|
||||||
|
{
|
||||||
|
$entry = new Entry();
|
||||||
|
$line1 = new Line(1, uniqid());
|
||||||
|
$line2 = new Line(2, uniqid());
|
||||||
|
|
||||||
|
$this->assertEquals(0, $entry->count());
|
||||||
|
$entry->addLine($line1);
|
||||||
|
$this->assertEquals(1, $entry->count());
|
||||||
|
$entry->addLine($line2);
|
||||||
|
$this->assertEquals(2, $entry->count());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOffsetExists(): void
|
||||||
|
{
|
||||||
|
$entry = new Entry();
|
||||||
|
$line = new Line(1, uniqid());
|
||||||
|
|
||||||
|
$this->assertArrayNotHasKey(0, $entry);
|
||||||
|
$this->assertEquals(0, $entry->count());
|
||||||
|
$entry->addLine($line);
|
||||||
|
$this->assertArrayHasKey(0, $entry);
|
||||||
|
$this->assertEquals($line, $entry[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOffsetGet(): void
|
||||||
|
{
|
||||||
|
$entry = new Entry();
|
||||||
|
$line = new Line(1, uniqid());
|
||||||
|
$entry->addLine($line);
|
||||||
|
|
||||||
|
// Exists
|
||||||
|
$this->assertEquals($line, $entry[0]);
|
||||||
|
|
||||||
|
// Does not exist -> "undefined array key" error
|
||||||
|
$this->assertArrayNotHasKey(1, $entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOffsetSet(): void
|
||||||
|
{
|
||||||
|
$entry = new Entry();
|
||||||
|
$line1 = new Line(1, uniqid());
|
||||||
|
|
||||||
|
$this->assertArrayNotHasKey(0, $entry);
|
||||||
|
$this->assertEquals(0, $entry->count());
|
||||||
|
$entry->addLine($line1);
|
||||||
|
$this->assertArrayHasKey(0, $entry);
|
||||||
|
$this->assertEquals($line1, $entry[0]);
|
||||||
|
|
||||||
|
// Overwrite $line1 on $entry[0] using the offsetSet
|
||||||
|
$line2 = new Line(2, uniqid());
|
||||||
|
$entry[0] = $line2;
|
||||||
|
$this->assertEquals($line2, $entry[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOffsetUnset(): void
|
||||||
|
{
|
||||||
|
$entry = new Entry();
|
||||||
|
$line = new Line(1, uniqid());
|
||||||
|
|
||||||
|
$this->assertArrayNotHasKey(0, $entry);
|
||||||
|
$this->assertEquals(0, $entry->count());
|
||||||
|
$entry->addLine($line);
|
||||||
|
$this->assertArrayHasKey(0, $entry);
|
||||||
|
$this->assertEquals($line, $entry[0]);
|
||||||
|
|
||||||
|
// Unset $line1 on $entry[0] using the offsetUnset
|
||||||
|
unset($entry[0]);
|
||||||
|
$this->assertArrayNotHasKey(0, $entry);
|
||||||
|
$this->assertArrayNotHasKey(1, $entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
test/tests/Log/File/PathLogFileTest.php
Normal file
17
test/tests/Log/File/PathLogFileTest.php
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Tests\Log\File;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\File\PathLogFile;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class PathLogFileTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testGetContent(): void
|
||||||
|
{
|
||||||
|
$path = __DIR__ . "/../../../data/simple.log";
|
||||||
|
$logFile = new PathLogFile($path);
|
||||||
|
|
||||||
|
$this->assertStringEqualsFile($path, $logFile->getContent());
|
||||||
|
}
|
||||||
|
}
|
||||||
18
test/tests/Log/File/StreamLogFileTest.php
Normal file
18
test/tests/Log/File/StreamLogFileTest.php
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Tests\Log\File;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\File\StreamLogFile;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class StreamLogFileTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testGetContent(): void
|
||||||
|
{
|
||||||
|
$path = __DIR__ . "/../../../data/simple.log";
|
||||||
|
$streamResource = fopen($path, 'r');
|
||||||
|
|
||||||
|
$logFile = new StreamLogFile($streamResource);
|
||||||
|
$this->assertStringEqualsFile($path, $logFile->getContent());
|
||||||
|
}
|
||||||
|
}
|
||||||
17
test/tests/Log/File/StringLogFileTest.php
Normal file
17
test/tests/Log/File/StringLogFileTest.php
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Tests\Log\File;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\File\StringLogFile;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class StringLogFileTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testGetContent(): void
|
||||||
|
{
|
||||||
|
$content = uniqid();
|
||||||
|
$logFile = new StringLogFile($content);
|
||||||
|
|
||||||
|
$this->assertEquals($content, $logFile->getContent());
|
||||||
|
}
|
||||||
|
}
|
||||||
33
test/tests/Log/LineTest.php
Normal file
33
test/tests/Log/LineTest.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Tests\Log;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\Line;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class LineTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testSetGetText(): void
|
||||||
|
{
|
||||||
|
$text = uniqid();
|
||||||
|
$line = new Line(1, "");
|
||||||
|
$this->assertSame($line, $line->setText($text));
|
||||||
|
$this->assertEquals($text, $line->getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetGetNumber(): void
|
||||||
|
{
|
||||||
|
$number = rand(0, 100);
|
||||||
|
$line = new Line(999, "");
|
||||||
|
$this->assertSame($line, $line->setNumber($number));
|
||||||
|
$this->assertEquals($number, $line->getNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test__toString(): void
|
||||||
|
{
|
||||||
|
$text = uniqid();
|
||||||
|
$line = new Line(1, $text);
|
||||||
|
$this->assertSame($line, $line->setText($text));
|
||||||
|
$this->assertEquals($text, (string)$line);
|
||||||
|
}
|
||||||
|
}
|
||||||
103
test/tests/Log/LogTest.php
Normal file
103
test/tests/Log/LogTest.php
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Tests\Log;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\Entry;
|
||||||
|
use Aternos\Codex\Log\Log;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class LogTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testAddEntry(): void
|
||||||
|
{
|
||||||
|
$log = new Log();
|
||||||
|
$entry = new Entry();
|
||||||
|
$this->assertSame($log, $log->addEntry($entry));
|
||||||
|
$this->assertEquals([$entry], $log->getEntries());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetGetEntries(): void
|
||||||
|
{
|
||||||
|
$log = new Log();
|
||||||
|
$entry = new Entry();
|
||||||
|
$this->assertSame($log, $log->setEntries([$entry]));
|
||||||
|
$this->assertEquals([$entry], $log->getEntries());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function testKey(): void
|
||||||
|
{
|
||||||
|
$log = new Log();
|
||||||
|
$entry = new Entry();
|
||||||
|
|
||||||
|
$log->addEntry($entry);
|
||||||
|
/** @noinspection PhpStatementHasEmptyBodyInspection */
|
||||||
|
foreach ($log as $ignored) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
$this->assertEquals(1, $log->key());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function testCount(): void
|
||||||
|
{
|
||||||
|
$log = new Log();
|
||||||
|
$entry1 = new Entry();
|
||||||
|
$entry2 = new Entry();
|
||||||
|
|
||||||
|
$this->assertEquals(0, $log->count());
|
||||||
|
$log->addEntry($entry1);
|
||||||
|
$this->assertEquals(1, $log->count());
|
||||||
|
$log->addEntry($entry2);
|
||||||
|
$this->assertEquals(2, $log->count());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOffsetExists(): void
|
||||||
|
{
|
||||||
|
$log = new Log();
|
||||||
|
$entry = new Entry();
|
||||||
|
|
||||||
|
$this->assertArrayNotHasKey(0, $log);
|
||||||
|
$this->assertEquals(0, $log->count());
|
||||||
|
$log->addEntry($entry);
|
||||||
|
$this->assertArrayHasKey(0, $log);
|
||||||
|
$this->assertEquals($entry, $log[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOffsetGet(): void
|
||||||
|
{
|
||||||
|
$log = new Log();
|
||||||
|
$entry = new Entry();
|
||||||
|
$log->addEntry($entry);
|
||||||
|
|
||||||
|
// Exists
|
||||||
|
$this->assertEquals($entry, $log[0]);
|
||||||
|
|
||||||
|
// Does not exist -> "undefined array key" error
|
||||||
|
$this->assertArrayNotHasKey(1, $log);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOffsetSet(): void
|
||||||
|
{
|
||||||
|
$log = new Log();
|
||||||
|
$entry1 = new Entry();
|
||||||
|
$log->addEntry($entry1);
|
||||||
|
|
||||||
|
// Overwrite $entry1 on $log[0] using the offsetSet
|
||||||
|
$entry2 = new Entry();
|
||||||
|
$log[0] = $entry2;
|
||||||
|
$this->assertEquals($entry2, $log[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOffsetUnset(): void
|
||||||
|
{
|
||||||
|
$log = new Log();
|
||||||
|
$entry = new Entry();
|
||||||
|
$log->addEntry($entry);
|
||||||
|
|
||||||
|
// Unset $entry on $log[0] using the offsetUnset
|
||||||
|
unset($log[0]);
|
||||||
|
$this->assertArrayNotHasKey(0, $log);
|
||||||
|
$this->assertArrayNotHasKey(1, $log);
|
||||||
|
}
|
||||||
|
}
|
||||||
47
test/tests/Parser/DefaultParserTest.php
Normal file
47
test/tests/Parser/DefaultParserTest.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Tests\Parser;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\Entry;
|
||||||
|
use Aternos\Codex\Log\File\PathLogFile;
|
||||||
|
use Aternos\Codex\Log\Line;
|
||||||
|
use Aternos\Codex\Log\Log;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class DefaultParserTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the log object expected from parsing data/simple.log
|
||||||
|
*
|
||||||
|
* @return Log
|
||||||
|
*/
|
||||||
|
protected function getSimpleExpectedLog(): Log
|
||||||
|
{
|
||||||
|
return (new Log())
|
||||||
|
->setLogFile(new PathLogFile(__DIR__ . '/../../data/simple.log'))
|
||||||
|
->addEntry((new Entry())
|
||||||
|
->addLine(new Line(1, "[01.01.1970 00:00:01] [Log/INFO] This is the first message containing information.")))
|
||||||
|
->addEntry((new Entry())
|
||||||
|
->addLine(new Line(2, "[01.01.1970 00:00:02] [Log/DEBUG] This is the second message containing a debug information.")))
|
||||||
|
->addEntry((new Entry())
|
||||||
|
->addLine(new Line(3, "[01.01.1970 00:00:03] [Log/WARN] This is the third message containing a warning information.")))
|
||||||
|
->addEntry((new Entry())
|
||||||
|
->addLine(new Line(4, "[01.01.1970 00:00:04] [Log/ERROR] This is the third message containing an error information.")))
|
||||||
|
->addEntry((new Entry())
|
||||||
|
->addLine(new Line(5, "This line continues the error entry to add even more information.")))
|
||||||
|
->addEntry((new Entry())
|
||||||
|
->addLine(new Line(6, "This line is also part of the error entry.")))
|
||||||
|
->addEntry((new Entry())
|
||||||
|
->addLine(new Line(7, "[01.01.1970 00:00:05] [Log/INFO] This is the last message of the log.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function testParse(): void
|
||||||
|
{
|
||||||
|
$logFile = new PathLogFile(__DIR__ . '/../../data/simple.log');
|
||||||
|
$log = (new Log())->setLogFile($logFile);
|
||||||
|
$log->parse();
|
||||||
|
|
||||||
|
$this->assertEquals($this->getSimpleExpectedLog(), $log);
|
||||||
|
}
|
||||||
|
}
|
||||||
76
test/tests/Parser/PatternParserTest.php
Normal file
76
test/tests/Parser/PatternParserTest.php
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Tests\Parser;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\Entry;
|
||||||
|
use Aternos\Codex\Log\File\PathLogFile;
|
||||||
|
use Aternos\Codex\Log\Level;
|
||||||
|
use Aternos\Codex\Log\Line;
|
||||||
|
use Aternos\Codex\Log\Log;
|
||||||
|
use Aternos\Codex\Parser\PatternParser;
|
||||||
|
use Aternos\Codex\Test\Src\Log\TestPatternLog;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class PatternParserTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the log object expected from parsing data/simple.log
|
||||||
|
*
|
||||||
|
* @return Log
|
||||||
|
*/
|
||||||
|
protected function getSimpleExpectedLog(): Log
|
||||||
|
{
|
||||||
|
return (new TestPatternLog())
|
||||||
|
->setLogFile(new PathLogFile(__DIR__ . '/../../data/simple.log'))
|
||||||
|
->addEntry((new Entry())->setLevel(Level::INFO)->setTime(1)->setPrefix("[01.01.1970 00:00:01] [Log/INFO]")
|
||||||
|
->addLine(new Line(1, "[01.01.1970 00:00:01] [Log/INFO] This is the first message containing information.")))
|
||||||
|
->addEntry((new Entry())->setLevel(Level::DEBUG)->setTime(2)->setPrefix("[01.01.1970 00:00:02] [Log/DEBUG]")
|
||||||
|
->addLine(new Line(2, "[01.01.1970 00:00:02] [Log/DEBUG] This is the second message containing a debug information.")))
|
||||||
|
->addEntry((new Entry())->setLevel(Level::WARNING)->setTime(3)->setPrefix("[01.01.1970 00:00:03] [Log/WARN]")
|
||||||
|
->addLine(new Line(3, "[01.01.1970 00:00:03] [Log/WARN] This is the third message containing a warning information.")))
|
||||||
|
->addEntry((new Entry())->setLevel(Level::ERROR)->setTime(4)->setPrefix("[01.01.1970 00:00:04] [Log/ERROR]")
|
||||||
|
->addLine(new Line(4, "[01.01.1970 00:00:04] [Log/ERROR] This is the third message containing an error information."))
|
||||||
|
->addLine(new Line(5, "This line continues the error entry to add even more information."))
|
||||||
|
->addLine(new Line(6, "This line is also part of the error entry.")))
|
||||||
|
->addEntry((new Entry())->setLevel(Level::INFO)->setTime(5)->setPrefix("[01.01.1970 00:00:05] [Log/INFO]")
|
||||||
|
->addLine(new Line(7, "[01.01.1970 00:00:05] [Log/INFO] This is the last message of the log.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function testParse(): void
|
||||||
|
{
|
||||||
|
$logFile = new PathLogFile(__DIR__ . '/../../data/simple.log');
|
||||||
|
$log = (new TestPatternLog())->setLogFile($logFile);
|
||||||
|
$log->parse();
|
||||||
|
|
||||||
|
$this->assertEquals($this->getSimpleExpectedLog(), $log);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testParseWithCustomParser(): void
|
||||||
|
{
|
||||||
|
$logFile = new PathLogFile(__DIR__ . '/../../data/simple.log');
|
||||||
|
$log = (new TestPatternLog())->setLogFile($logFile);
|
||||||
|
|
||||||
|
$patternParser = (new PatternParser())
|
||||||
|
->setPattern('/(\[([^\]]+)\] \[[^\/]+\/([^\]]+)\]).*/')
|
||||||
|
->setMatches([PatternParser::PREFIX, PatternParser::TIME, PatternParser::LEVEL])
|
||||||
|
->setTimeFormat('d.m.Y H:i:s');
|
||||||
|
$log->parse($patternParser);
|
||||||
|
|
||||||
|
$this->assertEquals($this->getSimpleExpectedLog(), $log);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetPattern(): void
|
||||||
|
{
|
||||||
|
$pattern = '/\[([^\]]+)\] \[[^\/]+\/([^\]]+)\].*/';
|
||||||
|
|
||||||
|
$patternParser = (new PatternParser())
|
||||||
|
->setPattern($pattern)
|
||||||
|
->setMatches([PatternParser::TIME, PatternParser::LEVEL])
|
||||||
|
->setTimezone(new \DateTimeZone('Europe/Berlin'))
|
||||||
|
->setTimeFormat('d.m.Y H:i:s');
|
||||||
|
|
||||||
|
$this->assertEquals($pattern, $patternParser->getPattern());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
35
test/tests/Printer/DefaultPrinterTest.php
Normal file
35
test/tests/Printer/DefaultPrinterTest.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Tests\Printer;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\Entry;
|
||||||
|
use Aternos\Codex\Log\File\PathLogFile;
|
||||||
|
use Aternos\Codex\Log\Line;
|
||||||
|
use Aternos\Codex\Log\Log;
|
||||||
|
use Aternos\Codex\Printer\DefaultPrinter;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class DefaultPrinterTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testPrint(): void
|
||||||
|
{
|
||||||
|
$logFile = new PathLogFile(__DIR__ . "/../../data/simple.log");
|
||||||
|
$log = new Log();
|
||||||
|
$log->setLogFile($logFile);
|
||||||
|
$log->parse();
|
||||||
|
|
||||||
|
$printer = new DefaultPrinter();
|
||||||
|
$printer->setLog($log);
|
||||||
|
$this->assertEquals($logFile->getContent(), trim($printer->print()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPrintEntry(): void
|
||||||
|
{
|
||||||
|
$text = uniqid();
|
||||||
|
$entry = (new Entry())->addLine(new Line(1, $text));
|
||||||
|
|
||||||
|
$printer = new DefaultPrinter();
|
||||||
|
$printer->setEntry($entry);
|
||||||
|
$this->assertEquals($text, trim($printer->print()));
|
||||||
|
}
|
||||||
|
}
|
||||||
38
test/tests/Printer/ModifiableDefaultPrinterTest.php
Normal file
38
test/tests/Printer/ModifiableDefaultPrinterTest.php
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Tests\Printer;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\Entry;
|
||||||
|
use Aternos\Codex\Log\File\StringLogFile;
|
||||||
|
use Aternos\Codex\Log\Line;
|
||||||
|
use Aternos\Codex\Log\Log;
|
||||||
|
use Aternos\Codex\Printer\ModifiableDefaultPrinter;
|
||||||
|
use Aternos\Codex\Test\Src\Printer\TestModification;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class ModifiableDefaultPrinterTest extends TestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
public function testPrint(): void
|
||||||
|
{
|
||||||
|
$logFile = new StringLogFile("This is foo!");
|
||||||
|
$log = new Log();
|
||||||
|
$log->setLogFile($logFile);
|
||||||
|
$log->parse();
|
||||||
|
|
||||||
|
$printer = new ModifiableDefaultPrinter();
|
||||||
|
$printer->addModification(new TestModification());
|
||||||
|
$printer->setLog($log);
|
||||||
|
$this->assertEquals("This is bar!", trim($printer->print()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPrintEntry(): void
|
||||||
|
{
|
||||||
|
$entry = (new Entry())->addLine(new Line(1, "This is foo!"));
|
||||||
|
|
||||||
|
$printer = new ModifiableDefaultPrinter();
|
||||||
|
$printer->setModifications([new TestModification()]);
|
||||||
|
$printer->setEntry($entry);
|
||||||
|
$this->assertEquals("This is bar!", trim($printer->print()));
|
||||||
|
}
|
||||||
|
}
|
||||||
25
test/tests/Printer/PatternModificationTest.php
Normal file
25
test/tests/Printer/PatternModificationTest.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aternos\Codex\Test\Tests\Printer;
|
||||||
|
|
||||||
|
use Aternos\Codex\Log\File\StringLogFile;
|
||||||
|
use Aternos\Codex\Log\Log;
|
||||||
|
use Aternos\Codex\Printer\ModifiableDefaultPrinter;
|
||||||
|
use Aternos\Codex\Printer\PatternModification;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class PatternModificationTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testPrint(): void
|
||||||
|
{
|
||||||
|
$logFile = new StringLogFile("This is foo!");
|
||||||
|
$log = new Log();
|
||||||
|
$log->setLogFile($logFile);
|
||||||
|
$log->parse();
|
||||||
|
|
||||||
|
$printer = new ModifiableDefaultPrinter();
|
||||||
|
$printer->addModification(new PatternModification('/foo/', 'bar'));
|
||||||
|
$printer->setLog($log);
|
||||||
|
$this->assertEquals("This is bar!", trim($printer->print()));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user