Explorar o código

added bcremer/line-reader framework

CauseFX %!s(int64=4) %!d(string=hai) anos
pai
achega
5c0505718e

+ 90 - 0
api/vendor/bcremer/line-reader/.github/workflows/build.yaml

@@ -0,0 +1,90 @@
+
+# https://docs.github.com/en/actions
+
+name: Build
+
+on:
+  pull_request:
+  push:
+    branches:
+      - main
+
+jobs:
+  unit-tests:
+    name: Tests
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        php-version:
+          - 7.3
+          - 7.4
+          - 8.0
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+
+      - name: Install PHP
+        uses: shivammathur/setup-php@v2
+        with:
+          coverage: none
+          php-version: ${{ matrix.php-version }}
+
+      - name: Determine composer cache directory
+        id: determine-composer-cache-directory
+        run: echo "::set-output name=directory::$(composer config cache-dir)"
+
+      - name: Cache dependencies installed with composer
+        uses: actions/cache@v2
+        with:
+          path: ${{ steps.determine-composer-cache-directory.outputs.directory }}
+          key: php-${{ matrix.php-version }}-composer-${{ hashFiles('composer.json') }}
+          restore-keys: php-${{ matrix.php-version }}-composer-
+
+      - name: Install dependencies
+        run: composer install --no-interaction --no-progress
+
+      - name: Run phpunit/phpunit
+        run: vendor/bin/phpunit
+
+  tests-with-coverage:
+    name: "Tests with coverage and PR Comments"
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        php-version:
+          - 7.4
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+
+      - name: Install PHP
+        uses: shivammathur/setup-php@v2
+        with:
+          coverage: pcov
+          php-version: ${{ matrix.php-version }}
+
+      - name: Setup problem matchers for PHP
+        run: echo "::add-matcher::${{ runner.tool_cache }}/php.json"
+
+      - name: Setup problem matchers for PHPUnit
+        run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
+
+      - name: Determine composer cache directory
+        id: determine-composer-cache-directory
+        run: echo "::set-output name=directory::$(composer config cache-dir)"
+
+      - name: Cache dependencies installed with composer
+        uses: actions/cache@v2
+        with:
+          path: ${{ steps.determine-composer-cache-directory.outputs.directory }}
+          key: php-${{ matrix.php-version }}-composer-${{ hashFiles('composer.json') }}
+          restore-keys: php-${{ matrix.php-version }}-composer-
+
+      - name: Install dependencies
+        run: composer install --no-interaction --no-progress
+
+      - name: Run phpunit/phpunit with code coverage
+        run: vendor/bin/phpunit --coverage-text --coverage-clover=clover.xml --coverage-xml=coverage/coverage.xml --log-junit=coverage/junit.xml
+
+      - name: Run infection
+        run: vendor/bin/infection --threads=4 --min-msi=81 --min-covered-msi=81 --coverage=coverage

+ 6 - 0
api/vendor/bcremer/line-reader/.gitignore

@@ -0,0 +1,6 @@
+vendor
+composer.lock
+clover.xml
+tests/testfile_*.txt
+infection-log.txt
+.phpunit.result.cache

+ 30 - 0
api/vendor/bcremer/line-reader/CHANGELOG.md

@@ -0,0 +1,30 @@
+## 1.1.0
+- Introduced support for PHP 7.4
+- Introduced support for PHP 8.0
+- Bumped minimum PHP Version to 7.3
+- Introduced github actions
+
+## 1.0.1
+
+- Introduced support for PHP 7.3
+
+## 1.0.0
+
+- First stable release
+- Bumped minimum PHP Version to 7.1
+- Introduced support for PHP 7.2
+- Adde scalar type hints
+
+## 0.2.0
+
+- Introduced support for PHP 7.1
+- Removed support for HHVM
+- Added humbug mutation testing
+- Fixed behaviour of trailing and leading newlines (Commit 95af4396e01a9294a9e82969192962220c1af0bd)
+- Exceptions are thrown even before the generator is read (Commit 54094f0c90772620ca24c636aaafb617d471bc68)
+
+## 0.1.0
+
+- Initial Release
+
+

+ 20 - 0
api/vendor/bcremer/line-reader/LICENSE

@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Benjamin Cremer
+
+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.

+ 134 - 0
api/vendor/bcremer/line-reader/README.md

@@ -0,0 +1,134 @@
+# LineReader
+
+[![Latest Version on Packagist][ico-version]][link-packagist]
+[![Software License][ico-license]](LICENSE.md)
+[![Build Status][ico-ghactions]][link-ghactions]
+
+LineReader is a library to read large files line by line in a memory efficient (constant) way.
+
+## Install
+
+Via Composer
+
+```bash
+$ composer require bcremer/line-reader
+```
+
+## Usage
+
+Given we have a textfile (`some/file.txt`) with lines like:
+
+```
+Line 1
+Line 2
+Line 3
+Line 4
+Line 5
+Line 6
+Line 7
+Line 8
+Line 9
+Line 10
+```
+
+Also let's assume the namespace is imported to keep the examples dense:
+
+```
+use Bcremer\LineReader\LineReader;
+```
+
+### Read forwards
+
+```php
+foreach (LineReader::readLines('some/file.txt') as $line) {
+    echo $line . "\n"
+}
+```
+
+The output will be:
+
+```
+Line 1
+Line 2
+Line 3
+Line 4
+Line 5
+...
+```
+
+To set an offset or a limit use the `\LimitIterator`:
+
+```php
+$lineGenerator = LineReader::readLines('some/file.txt');
+$lineGenerator = new \LimitIterator($lineGenerator, 2, 5);
+foreach ($lineGenerator as $line) {
+    echo $line . "\n"
+}
+```
+
+Will output line 3 to 7
+
+```
+Line 3
+Line 4
+Line 5
+Line 6
+Line 7
+```
+
+### Read backwards
+
+```php
+foreach (LineReader::readLinesBackwards('some/file.txt') as $line) {
+    echo $line;
+}
+```
+
+```
+Line 10
+Line 9
+Line 8
+Line 7
+Line 6
+...
+```
+
+Example: Read the last 5 lines in forward order:
+
+```php
+$lineGenerator = LineReader::readLinesBackwards('some/file.txt');
+$lineGenerator = new \LimitIterator($lineGenerator, 0, 5);
+
+$lines = array_reverse(iterator_to_array($lineGenerator));
+foreach ($line as $line) {
+    echo $line;
+}
+```
+
+```
+Line 6
+Line 7
+Line 8
+Line 9
+Line 10
+```
+
+## Testing
+
+```bash
+$ composer test
+```
+
+```bash
+$ TEST_MAX_LINES=200000 composer test
+```
+
+## License
+
+The MIT License (MIT). Please see [License File](LICENSE) for more information.
+
+[ico-version]: https://img.shields.io/packagist/v/bcremer/line-reader.svg?style=flat-square
+[ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square
+[link-packagist]: https://packagist.org/packages/bcremer/line-reader
+[ico-ghactions]: https://github.com/bcremer/LineReader/workflows/Build/badge.svg
+[link-ghactions]: https://github.com/bcremer/LineReader/actions?query=workflow%3ABuild

+ 32 - 0
api/vendor/bcremer/line-reader/composer.json

@@ -0,0 +1,32 @@
+{
+    "name": "bcremer/line-reader",
+    "description": "Read large files line by line in a memory efficient (constant) way.",
+    "type": "library",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Benjamin Cremer",
+            "email": "bc@benjamin-cremer.de"
+        }
+    ],
+    "require": {
+        "php": "^7.3|^7.4|^8.0"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "^9.4",
+        "infection/infection": "^0.18"
+    },
+    "autoload": {
+        "psr-4": {
+            "Bcremer\\LineReader\\": "src/"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Bcremer\\LineReaderTests\\": "tests/"
+        }
+    },
+    "scripts": {
+        "test": "phpunit"
+    }
+}

+ 11 - 0
api/vendor/bcremer/line-reader/infection.json.dist

@@ -0,0 +1,11 @@
+{
+    "source": {
+        "directories": [
+            "src"
+        ]
+    },
+    "logs": {
+        "text": "infection-log.txt"
+    },
+    "timeout": 2
+}

+ 24 - 0
api/vendor/bcremer/line-reader/phpunit.xml.dist

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
+         bootstrap="vendor/autoload.php"
+         beStrictAboutChangesToGlobalState="true"
+         beStrictAboutCoversAnnotation="true"
+         beStrictAboutOutputDuringTests="true"
+         beStrictAboutResourceUsageDuringSmallTests="true"
+         beStrictAboutTodoAnnotatedTests="true"
+         enforceTimeLimit="true"
+         executionOrder="random"
+         failOnRisky="true"
+         failOnWarning="true"
+         colors="true"
+         verbose="true">
+    <coverage>
+        <include>
+            <directory suffix=".php">./src</directory>
+        </include>
+    </coverage>
+    <testsuite name="LineReader tests">
+        <directory>tests</directory>
+    </testsuite>
+</phpunit>

+ 106 - 0
api/vendor/bcremer/line-reader/src/LineReader.php

@@ -0,0 +1,106 @@
+<?php
+
+namespace Bcremer\LineReader;
+
+final class LineReader
+{
+    /**
+     * Prevent instantiation
+     */
+    private function __construct() {}
+
+    /**
+     * @param string $filePath
+     * @return \Generator
+     * @throws \InvalidArgumentException if $filePath is not readable
+     */
+    public static function readLines(string $filePath): \Generator
+    {
+        if (!$fh = @fopen($filePath, 'r')) {
+            throw new \InvalidArgumentException('Cannot open file for reading: ' . $filePath);
+        }
+
+        return self::read($fh);
+    }
+
+    /**
+     * @param string $filePath
+     * @return \Generator
+     * @throws \InvalidArgumentException if $filePath is not readable
+     */
+    public static function readLinesBackwards(string $filePath): \Generator
+    {
+        if (!$fh = @fopen($filePath, 'r')) {
+            throw new \InvalidArgumentException('Cannot open file for reading: ' . $filePath);
+        }
+
+        $size = filesize($filePath);
+
+        return self::readBackwards($fh, $size);
+    }
+
+    /**
+     * @param resource $fh
+     * @return \Generator
+     */
+    private static function read($fh): \Generator
+    {
+        while (false !== $line = fgets($fh)) {
+            yield rtrim($line, "\n");
+        }
+
+        fclose($fh);
+    }
+
+    /**
+     * Read a file from the end using a buffer.
+     *
+     * This is way more efficient than using the naive method
+     * of reading the file backwards byte by byte looking for
+     * a newline character.
+     *
+     * @see http://stackoverflow.com/a/10494801/147634
+     * @param resource $fh
+     * @param int $pos
+     * @return \Generator
+     */
+    private static function readBackwards($fh, int $pos): \Generator
+    {
+        $buffer = null;
+        $bufferSize = 4096;
+
+        if ($pos === 0) {
+            return;
+        }
+
+        while (true) {
+            if (isset($buffer[1])) { // faster than count($buffer) > 1
+                yield array_pop($buffer);
+                continue;
+            }
+
+            if ($pos === 0) {
+                yield array_pop($buffer);
+                break;
+            }
+
+            if ($bufferSize > $pos) {
+                $bufferSize = $pos;
+                $pos = 0;
+            } else {
+                $pos -= $bufferSize;
+            }
+            fseek($fh, $pos);
+            $chunk = fread($fh, $bufferSize);
+            if ($buffer === null) {
+                // remove single trailing newline, rtrim cannot be used here
+                if (substr($chunk, -1) === "\n") {
+                    $chunk = substr($chunk, 0, -1);
+                }
+                $buffer = explode("\n", $chunk);
+            } else {
+                $buffer = explode("\n", $chunk . $buffer[0]);
+            }
+        }
+    }
+}

+ 178 - 0
api/vendor/bcremer/line-reader/tests/LineReaderTest.php

@@ -0,0 +1,178 @@
+<?php
+namespace Bcremer\LineReaderTests;
+
+use Bcremer\LineReader\LineReader;
+use PHPUnit\Framework\TestCase;
+
+class LineReaderTest extends TestCase
+{
+    private static $maxLines;
+    private static $testFile;
+
+    public static function setUpBeforeClass(): void
+    {
+        self::$maxLines = (int)getenv('TEST_MAX_LINES') ?: 10000;
+        self::$testFile = __DIR__.'/testfile_'.self::$maxLines.'.txt';
+
+        if (is_file(self::$testFile)) {
+            return;
+        }
+
+        $fh = fopen(self::$testFile, 'w');
+        for ($i = 1; $i <= self::$maxLines; $i++) {
+            fwrite($fh, "Line $i\n");
+        }
+        fclose($fh);
+    }
+
+    public function testReadLinesThrowsException(): void
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('Cannot open file for reading: /tmp/invalid-file.txt');
+
+        LineReader::readLines('/tmp/invalid-file.txt');
+    }
+
+    public function testReadLinesBackwardsThrowsException(): void
+    {
+        $this->expectException(\InvalidArgumentException::class);
+        $this->expectExceptionMessage('Cannot open file for reading: /tmp/invalid-file.txt');
+
+        LineReader::readLinesBackwards('/tmp/invalid-file.txt');
+    }
+
+    public function testReadsAllLines(): void
+    {
+        $result = LineReader::readLines(self::$testFile);
+
+        self::assertInstanceOf(\Traversable::class, $result);
+
+        $firstLine = 1;
+        $lastLine = self::$maxLines;
+        $lineCount = self::$maxLines;
+        $this->assertLines($result, $firstLine, $lastLine, $lineCount);
+    }
+
+    public function testReadsLinesByStartline(): void
+    {
+        $lineGenerator = LineReader::readLines(self::$testFile);
+        $lineGenerator = new \LimitIterator($lineGenerator, 50);
+
+        $firstLine = 51;
+        $lastLine = self::$maxLines;
+        $lineCount = self::$maxLines-50;
+        $this->assertLines($lineGenerator, $firstLine, $lastLine, $lineCount);
+    }
+
+    public function testReadsLinesByLimit(): void
+    {
+        $lineGenerator = LineReader::readLines(self::$testFile);
+        $lineGenerator = new \LimitIterator($lineGenerator, 50, 100);
+
+        $firstLine = 51;
+        $lastLine = 150;
+        $lineCount = 100;
+        $this->assertLines($lineGenerator, $firstLine, $lastLine, $lineCount);
+    }
+
+    public function testReadsLinesBackwards(): void
+    {
+        $lineGenerator = LineReader::readLinesBackwards(self::$testFile);
+
+        $firstLine = self::$maxLines;
+        $lastLine = 1;
+        $lineCount = self::$maxLines;
+        $this->assertLines($lineGenerator, $firstLine, $lastLine, $lineCount);
+    }
+
+    public function testReadsLinesBackwardsWithOffsetAndLimit(): void
+    {
+        $lineGenerator = LineReader::readLinesBackwards(self::$testFile);
+        $lineGenerator = new \LimitIterator($lineGenerator, 10, 50);
+
+        $firstLine = self::$maxLines-10;
+        $lastLine = self::$maxLines-59;
+        $lineCount = 50;
+        $this->assertLines($lineGenerator, $firstLine, $lastLine, $lineCount);
+    }
+
+    public function testEmptyFile(): void
+    {
+        $testFile = __DIR__.'/testfile_empty.txt';
+        $content = '';
+        file_put_contents($testFile, $content);
+
+        $lineGenerator = LineReader::readLines($testFile);
+        self::assertSame([], iterator_to_array($lineGenerator));
+
+        $lineGenerator = LineReader::readLinesBackwards($testFile);
+        self::assertSame([], iterator_to_array($lineGenerator));
+    }
+
+    public function testFileWithLeadingAndTrailingNewlines(): void
+    {
+        $testFile = __DIR__.'/testfile_space.txt';
+
+        $content = <<<CONTENT
+
+
+Line 1
+
+
+Line 4
+Line 5
+
+
+CONTENT;
+
+        file_put_contents($testFile, $content);
+
+        self::assertSame(
+            [
+                '',
+                '',
+                'Line 1',
+                '',
+                '',
+                'Line 4',
+                'Line 5',
+                '',
+            ],
+            iterator_to_array(LineReader::readLines($testFile))
+        );
+
+        self::assertSame(
+            [
+                '',
+                'Line 5',
+                'Line 4',
+                '',
+                '',
+                'Line 1',
+                '',
+                '',
+            ],
+            iterator_to_array(LineReader::readLinesBackwards($testFile))
+        );
+    }
+
+    /**
+     * Runs the generator and asserts on first, last and the total line count
+     *
+     * @param \Traversable $generator
+     */
+    private function assertLines(\Traversable $generator, string $firstLine, int $lastLine, int $lineCount): void
+    {
+        $count = 0;
+        $line = '';
+        foreach ($generator as $line) {
+            if ($count === 0) {
+                self::assertSame("Line $firstLine", $line, 'Expect first line');
+            }
+            $count++;
+        }
+
+        self::assertSame("Line $lastLine", $line, 'Expect last line');
+        self::assertSame($lineCount, $count, 'Expect total line count');
+    }
+}