diff --git a/.gitattributes b/.gitattributes index eecf37d..db3a60e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,7 +2,5 @@ tests/ export-ignore .editorconfig export-ignore .gitattributes export-ignore .gitignore export-ignore -.scrutinizer.yml export-ignore -.travis.yml export-ignore CONTRIBUTING.md export-ignore phpunit.xml.dist export-ignore diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..f7b8104 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + open-pull-requests-limit: 5 + - package-ecosystem: composer + directory: / + schedule: + interval: monthly + open-pull-requests-limit: 5 + versioning-strategy: widen diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..e34a488 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,61 @@ +name: Test + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +permissions: + contents: read + +jobs: + test: + strategy: + fail-fast: false + matrix: + php-versions: [ '8.2', '8.3', '8.4', '8.5' ] + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + coverage: none + tools: composer:v2 + extensions: pdo_sqlite + + - name: Validate Composer metadata + run: | + if [ -f composer.lock ]; then + composer validate --strict --no-interaction + else + composer validate --strict --no-check-lock --no-interaction + fi + + - name: Get Composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT" + + - name: Cache Composer packages + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ matrix.php-versions }}-${{ hashFiles('**/composer.json', '**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer-${{ matrix.php-versions }}- + + - name: Install dependencies + run: | + if [ -f composer.lock ]; then + composer install --prefer-dist --no-progress --no-interaction + else + composer update --prefer-dist --no-progress --no-interaction + fi + + - name: Run test suite + run: vendor/bin/phpunit diff --git a/.scrutinizer.yml b/.scrutinizer.yml deleted file mode 100644 index acbda73..0000000 --- a/.scrutinizer.yml +++ /dev/null @@ -1,7 +0,0 @@ -checks: - php: - code_rating: true - duplication: true - -tools: - external_code_coverage: true diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 2b1cd46..0000000 --- a/.travis.yml +++ /dev/null @@ -1,28 +0,0 @@ -language: php - -php: - - 5.6 - - 7.0 - - 7.1 - - hhvm - -matrix: - allow_failures: - - php: 7.0 - - php: hhvm - include: - - php: 5.6 - env: - - COMPOSER_FLAGS="--prefer-lowest --prefer-stable" - - COVERAGE=true - - PHPUNIT_FLAGS="--coverage-clover=coverage.clover" - -install: - - travis_retry composer update ${COMPOSER_FLAGS} --prefer-source --no-interaction - -script: - - vendor/bin/phpunit ${PHPUNIT_FLAGS} - -after_script: - - if [[ "$COVERAGE" = true ]]; then wget https://scrutinizer-ci.com/ocular.phar; fi - - if [[ "$COVERAGE" = true ]]; then php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi diff --git a/README.md b/README.md index 3155c75..9a4240f 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ # Port PDO adapter [![Latest Version](https://img.shields.io/github/release/portphp/pdo.svg?style=flat-square)](https://github.com/portphp/pdo/releases) -[![Build Status](https://travis-ci.org/portphp/pdo.svg)](https://travis-ci.org/portphp/pdo) -[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/portphp/pdo/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/portphp/pdo/?branch=master) -[![Code Coverage](https://scrutinizer-ci.com/g/portphp/pdo/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/portphp/pdo/?branch=master) +[![CI](https://github.com/portphp/pdo/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/portphp/pdo/actions/workflows/test.yml) +[![PHP Version](https://img.shields.io/packagist/php-v/portphp/pdo.svg?style=flat-square)](https://packagist.org/packages/portphp/pdo) + +**Requirements:** PHP ^8.2 (tested on 8.2–8.5). Requires `portphp/portphp` ^2.0. PDO adapter for [Port](https://github.com/portphp). @@ -22,7 +23,7 @@ of the Composer documentation. ## Documentation -Documentation is available at http://docs.portphp.org. +Documentation is available at https://portphp.readthedocs.io. ## Issues and feature requests @@ -34,11 +35,6 @@ Contributions are very welcome. Please see [CONTRIBUTING.md](CONTRIBUTING.md) fo details. Thanks to [everyone who has contributed](https://github.com/portphp/pdo/graphs/contributors) already. -## Security - -If you discover any security-related issues, please contact us at -[security@portphp.org](mailto:security@portphp.org). - ## License This package is licensed under the [MIT license](LICENSE). diff --git a/UPGRADE-2.0.md b/UPGRADE-2.0.md new file mode 100644 index 0000000..5e7002e --- /dev/null +++ b/UPGRADE-2.0.md @@ -0,0 +1,5 @@ +# Upgrade from 1.x to 2.0 + +- Minimum PHP is **8.2** (`^8.2`). +- Requires `portphp/portphp` **^2.0**. +- CI is GitHub Actions (Travis/Scrutinizer removed). diff --git a/composer.json b/composer.json index 4ee106a..01860e2 100644 --- a/composer.json +++ b/composer.json @@ -14,18 +14,18 @@ }, { "name": "Community contributors", - "homepage": "https://github.com/portphp/dbal-adapter/graphs/contributors" + "homepage": "https://github.com/portphp/pdo/graphs/contributors" } ], "support": { "issues": "https://github.com/portphp/portphp/issues", - "source": "https://github.com/portphp/dbal-adapter", - "docs": "https://docs.portphp.org" + "source": "https://github.com/portphp/pdo", + "docs": "https://portphp.readthedocs.io" }, "require": { - "php": ">=5.4.0", + "php": "^8.2", "ext-pdo": "*", - "portphp/portphp": "^1.0.0" + "portphp/portphp": "^2.0" }, "autoload": { "psr-4": { @@ -33,8 +33,8 @@ } }, "require-dev": { - "ext-sqlite3": "*", - "phpunit/phpunit": "^4.0" + "ext-pdo_sqlite": "*", + "phpunit/phpunit": "^9.6" }, "autoload-dev": { "psr-4": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 1a4db5c..fc3ff71 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,20 +1,14 @@ - - + beStrictAboutOutputDuringTests="true" + failOnRisky="false" + failOnWarning="true"> - - ./tests/ + + tests diff --git a/src/PdoWriter.php b/src/PdoWriter.php index dbd6e85..dc9f78a 100644 --- a/src/PdoWriter.php +++ b/src/PdoWriter.php @@ -55,7 +55,7 @@ public function __construct(\PDO $pdo, $tableName) } } - public function prepare() + public function prepare(): void { $this->stack = []; $this->statement = null; @@ -64,7 +64,7 @@ public function prepare() /** * {@inheritdoc} */ - public function writeItem(array $item) + public function writeItem(array $item): void { if (null === $this->statement) { try { @@ -75,21 +75,21 @@ public function writeItem(array $item) substr(str_repeat('?,', count($item)), 0, -1) )); } catch (\PDOException $e) { - throw new WriterException('Failed to send query', null, $e); + throw new WriterException('Failed to send query', 0, $e); } } $this->stack[] = array_values($item); } - public function finish() + public function finish(): void { $this->flush(); - return $this; + } - public function flush() + public function flush(): void { $this->pdo->beginTransaction(); @@ -102,7 +102,7 @@ public function flush() $this->pdo->commit(); } catch (\PDOException $e) { $this->pdo->rollBack(); - throw new WriterException('Failed to write to database', null, $e); + throw new WriterException('Failed to write to database', 0, $e); } } } diff --git a/tests/PdoReaderTest.php b/tests/PdoReaderTest.php index a5a9660..542be6d 100644 --- a/tests/PdoReaderTest.php +++ b/tests/PdoReaderTest.php @@ -4,7 +4,7 @@ use Port\Pdo\PdoReader; -class PdoReaderTest extends \PHPUnit_Framework_TestCase +class PdoReaderTest extends \PHPUnit\Framework\TestCase { public function testCount() { @@ -15,7 +15,7 @@ public function testIterate() { $i=1; foreach ($this->getReader() as $row) { - $this->assertInternalType('array', $row); + $this->assertIsArray( $row); $this->assertEquals('user-'.$i, $row['username']); $i++; } @@ -24,11 +24,18 @@ public function testIterate() public function testReaderRewindWorksCorrectly() { $reader = $this->getReader(); + $count1 = 0; foreach ($reader as $row) { + $count1++; } + $count2 = 0; foreach ($reader as $row) { + $count2++; } + + $this->assertSame($count1, $count2); + $this->assertSame(100, $count1); } public function getConnection() diff --git a/tests/PdoWriterTest.php b/tests/PdoWriterTest.php index e966f9e..62471db 100644 --- a/tests/PdoWriterTest.php +++ b/tests/PdoWriterTest.php @@ -2,35 +2,36 @@ namespace Port\Tests\Writer; +use Port\Exception\WriterException; use Port\Pdo\PdoWriter; +use PHPUnit\Framework\TestCase; -class PdoWriterTest extends \PHPUnit_Framework_TestCase +class PdoWriterTest extends TestCase { /** * @var \PDO */ private $pdo; - public function setUp() + public function setUp(): void { $this->pdo = new \PDO('sqlite::memory:'); - $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); //important - $this->pdo->exec('DROP TABLE IF EXISTS `example`'); - $this->pdo->exec('CREATE TABLE `example` (a TEXT, b TEXT)'); + $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + $this->pdo->exec('DROP TABLE IF EXISTS example'); + $this->pdo->exec('CREATE TABLE example (a TEXT, b TEXT)'); } public function testValidWriteItem() { $writer = new PdoWriter($this->pdo, 'example'); $writer->prepare(); - $writer->writeItem(array('a' => 'foo', 'b' => 'bar')); + $writer->writeItem(['a' => 'foo', 'b' => 'bar']); $writer->finish(); - $stmnt = $this->pdo->query('SELECT * FROM `example`'); + $stmnt = $this->pdo->query('SELECT * FROM example'); $this->assertEquals( - array(array('a'=>'foo', 'b'=>'bar')), - $stmnt->fetchAll(\PDO::FETCH_ASSOC), - 'database does not contain expected row' + [['a' => 'foo', 'b' => 'bar']], + $stmnt->fetchAll(\PDO::FETCH_ASSOC) ); } @@ -38,69 +39,48 @@ public function testValidWriteMultiple() { $writer = new PdoWriter($this->pdo, 'example'); $writer->prepare(); - $writer->writeItem(array('a' => 'foo', 'b' => 'bar')); - $writer->writeItem(array('a' => 'cat', 'b' => 'dog')); - $writer->writeItem(array('a' => 'ac', 'b' => 'dc')); + $writer->writeItem(['a' => 'foo', 'b' => 'bar']); + $writer->writeItem(['a' => 'cat', 'b' => 'dog']); + $writer->writeItem(['a' => 'ac', 'b' => 'dc']); $writer->finish(); - $stmnt = $this->pdo->query('SELECT * FROM `example`'); + $stmnt = $this->pdo->query('SELECT * FROM example'); $this->assertEquals( - array(array('a'=>'foo', 'b'=>'bar'), array('a'=>'cat', 'b'=>'dog'), array('a'=>'ac', 'b'=>'dc')), - $stmnt->fetchAll(\PDO::FETCH_ASSOC), - 'database does not contain all expected rows' + [ + ['a' => 'foo', 'b' => 'bar'], + ['a' => 'cat', 'b' => 'dog'], + ['a' => 'ac', 'b' => 'dc'], + ], + $stmnt->fetchAll(\PDO::FETCH_ASSOC) ); } - /** - * @expectedException \Port\Exception\WriterException - */ public function testWriteTooManyValues() { + $this->expectException(WriterException::class); + $writer = new PdoWriter($this->pdo, 'example'); $writer->prepare(); - $writer->writeItem(array('foo', 'bar', 'baz')); //expects two + $writer->writeItem(['a' => 'foo', 'b' => 'bar', 'c' => 'baz']); $writer->finish(); } - /** - * @expectedException \Port\Exception\WriterException - */ public function testWriteToNonexistentTable() { + $this->expectException(WriterException::class); + $writer = new PdoWriter($this->pdo, 'foobar'); $writer->prepare(); - $writer->writeItem(array('foo', 'bar')); + $writer->writeItem(['a' => 'foo', 'b' => 'bar']); $writer->finish(); } - /** - * Tests PDO instance with silent errors. - * - * @expectedException \Port\Exception\WriterException - */ - public function testStatementCreateFailureWithNoException() + public function testRequiresExceptionErrorMode() { - $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT); - - $writer = new PdoWriter($this->pdo, 'foob`ar'); - $writer->prepare(); - $writer->writeItem(array('foo', 'bar')); - $writer->finish(); - } + $this->expectException(WriterException::class); + $this->expectExceptionMessage('Please set the pdo error mode to PDO::ERRMODE_EXCEPTION'); - /** - * Tests PDO instance with silent errors. First inert prepares the statement, second creates an exception. - * - * @expectedException \Port\Exception\WriterException - */ - public function testWriteFailureWithNoException() - { $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT); - - $writer = new PdoWriter($this->pdo, 'example'); - $writer->prepare(); - $writer->writeItem(array('foo', 'bar')); - $writer->writeItem(array('foo', 'bar', 'baz')); - $writer->finish(); + new PdoWriter($this->pdo, 'example'); } }