From 05276f83eebaadae9b7fc66d11ba835450a0d50a Mon Sep 17 00:00:00 2001 From: Jan Skrasek Date: Thu, 4 Nov 2021 19:37:02 +0100 Subject: [PATCH] build on GitHub Actions --- .appveyor.yml | 40 ---- .github/workflows/build.yml | 213 ++++++++++++++++++ .phpstan.neon | 2 +- .travis.yml | 58 ----- composer.json | 6 +- .../NextrasDbalExtension.php | 2 +- src/Connection.php | 1 + src/Drivers/Mysqli/MysqliDriver.php | 1 - src/Drivers/Pgsql/PgsqlDriver.php | 8 +- src/Drivers/Pgsql/PgsqlResultAdapter.php | 4 +- src/Drivers/Sqlsrv/SqlsrvDriver.php | 5 +- tests/cases/integration/platform.mysqli.phpt | 23 +- tests/databases.appveyor.ini | 6 - tests/databases.github.ini | 47 ++++ tests/php.unix-sample.ini | 3 - tests/php.win-sample.ini | 5 - tests/run.sh | 2 +- 17 files changed, 294 insertions(+), 132 deletions(-) delete mode 100644 .appveyor.yml create mode 100644 .github/workflows/build.yml delete mode 100644 .travis.yml delete mode 100644 tests/databases.appveyor.ini create mode 100644 tests/databases.github.ini delete mode 100644 tests/php.unix-sample.ini delete mode 100644 tests/php.win-sample.ini diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index 8cf11faa..00000000 --- a/.appveyor.yml +++ /dev/null @@ -1,40 +0,0 @@ -build: off -cache: - - c:\php -> .appveyor.yml - - '%LOCALAPPDATA%\Composer\files -> .appveyor.yml' - -clone_folder: c:\projects\dbal - -services: - - mssql2016 - -init: - - SET PATH=c:\php;%PATH% - - SET PHP=1 - - SET ANSICON=121x90 (121x90) - -install: - # Install PHP - - IF EXIST c:\php (SET PHP=0) ELSE (mkdir c:\php) - - IF %PHP%==1 cd c:\php - - IF %PHP%==1 curl -o php-7.2.5-Win32-VC15-x64.zip https://windows.php.net/downloads/releases/archives/php-7.2.5-Win32-VC15-x64.zip - - IF %PHP%==1 7z x php-7.2.5-Win32-VC15-x64.zip >nul - - IF %PHP%==1 echo extension_dir=ext >> php.ini - - IF %PHP%==1 echo extension=php_openssl.dll >> php.ini - - IF %PHP%==1 echo extension=php_sqlsrv_ts.dll >> php.ini - - IF %PHP%==1 appveyor DownloadFile https://github.com/Microsoft/msphpsql/releases/download/v5.2.0/Windows-7.2.zip - - IF %PHP%==1 7z x Windows-7.2.zip >nul - - IF %PHP%==1 copy Windows-7.2\x64\php_sqlsrv_72_ts.dll ext\php_sqlsrv_ts.dll - - IF %PHP%==1 del /Q *.zip - - cd c:\projects\dbal - - # Install Nette Tester - - appveyor DownloadFile https://getcomposer.org/composer.phar - - php composer.phar install --prefer-dist --no-interaction --no-progress - - # Create databases.ini - - copy tests\databases.appveyor.ini tests\databases.ini - -test_script: - - sqlcmd -S ".\SQL2016" -U sa -P Password12! -Q "CREATE DATABASE nextras_dbal_test" -d "master" - - vendor\bin\tester -p php -c tests\php.win-sample.ini --setup tests\inc\setup.php tests\cases diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..03ae4e92 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,213 @@ +name: "Build" + +on: + pull_request: + types: [ "opened", "synchronize", "edited", "reopened" ] + paths-ignore: + - doc/** + push: + paths-ignore: + - doc/** + branches: + - main + - v*.* + +env: + php-extensions: mbstring, intl, mysqli, pgsql, sqlsrv-5.10.0-beta1, pdo_sqlsrv-5.10.0-beta1 + php-extensions-key: v1 + php-tools: "composer:v2, pecl" + +jobs: + phpstan: + name: PHPStan + + runs-on: ubuntu-latest + + strategy: + matrix: + php-version: [ '7.4', '8.0', '8.1' ] + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP with pecl extension + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + + - name: Get composer cache directory + id: composercache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composercache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --prefer-dist + + - name: Run PHPStan + run: composer phpstan + + tests: + name: Tests + + strategy: + fail-fast: false + matrix: + php-version: [ '7.1', '7.2', '7.3', '7.4', '8.0', '8.1' ] + + runs-on: ubuntu-latest + + services: + mysql57: + image: mysql:5.7 + env: + MYSQL_DATABASE: nextras_dbal_test + MYSQL_ROOT_PASSWORD: root + ports: + - 3306:3306 + options: >- + --health-cmd "mysqladmin ping -ppass" + --health-interval 10s + --health-start-period 10s + --health-timeout 5s + --health-retries 10 + mysql80: + image: mysql:8.0 + ports: + - 3307:3306 + options: --health-cmd="mysqladmin ping -ppass" --health-interval=10s --health-timeout=5s --health-retries=5 -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=nextras_dbal_test --entrypoint sh mysql:8 -c "exec docker-entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password" + mariadb105: + image: mariadb:10.5 + env: + MYSQL_DATABASE: nextras_dbal_test + MYSQL_ROOT_PASSWORD: root + ports: + - 3308:3306 + options: >- + --health-cmd "mysqladmin ping -ppass" + --health-interval 10s + --health-start-period 10s + --health-timeout 5s + --health-retries 10 + postgres96: + image: postgres:9.6 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: nextras_dbal_test + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + postgres13: + image: postgres:13 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: nextras_dbal_test + ports: + - 5433:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + mssql: + image: mcr.microsoft.com/mssql/server:latest + env: + ACCEPT_EULA: Y + SA_PASSWORD: YourStrong!Passw0rd + MSSQL_PID: Developer + ports: + - 1433:1433 + options: >- + --name=mssql + --health-cmd "/opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P 'YourStrong!Passw0rd' -Q 'SELECT 1'" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Create MS SQL Database + run: docker exec -i mssql /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P 'YourStrong!Passw0rd' -Q 'CREATE DATABASE nextras_dbal_test' + + - name: Setup PHP cache environment + id: php-extensions-cache + uses: shivammathur/cache-extensions@v1 + with: + php-version: ${{ matrix.php-version }} + extensions: ${{ env.php-extensions }} + key: ${{ env.php-extensions-key }} + + - name: Cache PHP extensions + uses: actions/cache@v2 + with: + path: ${{ steps.php-extensions-cache.outputs.dir }} + key: ${{ steps.php-extensions-cache.outputs.key }} + restore-keys: ${{ steps.php-extensions-cache.outputs.key }} + + - name: Setup PHP with pecl extension + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: ${{ env.php-extensions }} + tools: ${{ env.php-tools }} + coverage: pcov + + - name: Get composer cache directory + id: composercache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composercache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --prefer-dist --no-interaction --no-progress --no-suggest + + - name: Init config + run: cp ./tests/databases.github.ini ./tests/databases.ini + + - name: Tests + run: ./tests/run.sh ./tests/cases --coverage ./clover.xml --coverage-src ./src + + - name: Print failed expectations + if: ${{ failure() }} + run: | + find tests -name \*.actual -exec echo "--- {}" \; -exec cat {} \; -exec echo \; -exec echo \; && \ + find tests -name \*.log -exec echo "--- {}" \; -exec cat {} \; -exec echo \; -exec echo \; + + - name: Send coverage report + env: + COVERALLS_PARALLEL: "true" + COVERALLS_FLAG_NAME: run-${{ matrix.php-version }} + COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + composer global require php-coveralls/php-coveralls --prefer-dist --no-interaction --no-progress --no-suggest && \ + php-coveralls -v --coverage_clover=./clover.xml --json_path=./coveralls-upload.json + + coverage-finish: + name: Code coverage finish + needs: tests + runs-on: ubuntu-latest + steps: + - name: Coveralls Finished + env: + COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + curl -k "https://coveralls.io/webhook?repo_name=$GITHUB_REPOSITORY&repo_token=$COVERALLS_REPO_TOKEN" -d "payload[build_num]=$GITHUB_RUN_ID&payload[status]=done" diff --git a/.phpstan.neon b/.phpstan.neon index 28e7b136..4d23eabe 100644 --- a/.phpstan.neon +++ b/.phpstan.neon @@ -2,7 +2,7 @@ includes: - phar://%rootDir%/phpstan.phar/conf/bleedingEdge.neon parameters: - level: max + level: 8 paths: - src diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index dc3defd1..00000000 --- a/.travis.yml +++ /dev/null @@ -1,58 +0,0 @@ -language: php - -php: - - 7.1 - - 7.2 - - 7.3 - - 7.4 - -services: - - mysql - - postgresql - -cache: - directories: - - $HOME/.composer/cache - -before_install: - - phpenv config-rm xdebug.ini || true - - # Create php.ini & databases.ini - - cp ./tests/php.unix-sample.ini ./tests/php.ini - - cp ./tests/databases.sample.ini ./tests/databases.ini - - # Create MySQL & PostgreSQL database - - psql -c 'CREATE DATABASE nextras_dbal_test' -U postgres - - sleep 2 - - mysql -e 'CREATE DATABASE nextras_dbal_test;' - - mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql - - mysql -u root -e "SET PASSWORD FOR 'root'@'localhost' = PASSWORD('')" - -install: - - travis_retry composer update --no-interaction --prefer-dist --no-progress - -jobs: - fast_finish: true - include: - - name: Lowest Dependencies - install: - - travis_retry composer update --prefer-lowest --prefer-stable --no-interaction --prefer-dist --no-progress - -before_script: - - if [ "$TRAVIS_PHP_VERSION" == "7.4" ]; then NTESTER_FLAGS="-p phpdbg --coverage ./coverage.xml --coverage-src ./src"; fi - -script: - - ./tests/run.sh -s $NTESTER_FLAGS ./tests/cases - - if [ "$TRAVIS_PHP_VERSION" == "7.4" ]; then composer phpstan; fi - -after_script: - - if [ "$TRAVIS_PHP_VERSION" == "7.4" ]; then - wget https://github.com/satooshi/php-coveralls/releases/download/v2.0.0/php-coveralls.phar - && php php-coveralls.phar --verbose --config tests/.coveralls.yml - || true; - fi - -after_failure: - # Print *.actual content & log content - - for i in $(find tests -name \*.actual); do echo "--- $i"; cat $i; echo; echo; done - - for i in $(find tests -name \*.log); do echo "--- $i"; cat $i; echo; echo; done diff --git a/composer.json b/composer.json index 4e926434..1e4a3e50 100644 --- a/composer.json +++ b/composer.json @@ -23,9 +23,9 @@ "nette/finder": "~2.5", "nette/neon": "~3.0", "phpstan/extension-installer": "1.0.5", - "phpstan/phpstan": "0.12.48", - "phpstan/phpstan-deprecation-rules": "0.12.5", - "phpstan/phpstan-strict-rules": "0.12.5", + "phpstan/phpstan": "1.0.0", + "phpstan/phpstan-deprecation-rules": "1.0.0", + "phpstan/phpstan-strict-rules": "1.0.0", "symfony/config": "~4.4 || ~5.0", "symfony/dependency-injection": "~4.4 || ~5.0", "symfony/http-kernel": "~4.4 || ~5.0", diff --git a/src/Bridges/SymfonyBundle/DependencyInjection/NextrasDbalExtension.php b/src/Bridges/SymfonyBundle/DependencyInjection/NextrasDbalExtension.php index 21b948a2..6d411136 100644 --- a/src/Bridges/SymfonyBundle/DependencyInjection/NextrasDbalExtension.php +++ b/src/Bridges/SymfonyBundle/DependencyInjection/NextrasDbalExtension.php @@ -19,7 +19,7 @@ class NextrasDbalExtension extends Extension */ public function load(array $configs, ContainerBuilder $container): void { - $configuration = new Configuration($container->getParameter('kernel.debug')); + $configuration = new Configuration((bool) $container->getParameter('kernel.debug')); $config = $this->processConfiguration($configuration, $configs); $defaultConnectionName = $config['default_connection']; diff --git a/src/Connection.php b/src/Connection.php index b6339f05..7c19f577 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -348,6 +348,7 @@ private function createDriver(): IDriver return $this->config['driver']; } else { $name = ucfirst($this->config['driver']); + /** @var class-string $class */ $class = "Nextras\\Dbal\\Drivers\\{$name}\\{$name}Driver"; return new $class(); } diff --git a/src/Drivers/Mysqli/MysqliDriver.php b/src/Drivers/Mysqli/MysqliDriver.php index 33d78580..fd0041fb 100644 --- a/src/Drivers/Mysqli/MysqliDriver.php +++ b/src/Drivers/Mysqli/MysqliDriver.php @@ -378,7 +378,6 @@ public function convertIdentifierToSql(string $value): string public function convertDateTimeToSql(DateTimeInterface $value): string { $valueTimezone = $value->getTimezone(); - assert($value instanceof DateTime || $value instanceof DateTimeImmutable); assert($valueTimezone !== false); // @phpstan-ignore-line if ($valueTimezone->getName() !== $this->connectionTz->getName()) { if ($value instanceof DateTimeImmutable) { diff --git a/src/Drivers/Pgsql/PgsqlDriver.php b/src/Drivers/Pgsql/PgsqlDriver.php index 5e9f4ef9..e156e6e1 100644 --- a/src/Drivers/Pgsql/PgsqlDriver.php +++ b/src/Drivers/Pgsql/PgsqlDriver.php @@ -18,6 +18,7 @@ use Nextras\Dbal\Drivers\Exception\UniqueConstraintViolationException; use Nextras\Dbal\Drivers\IDriver; use Nextras\Dbal\Exception\InvalidArgumentException; +use Nextras\Dbal\Exception\InvalidStateException; use Nextras\Dbal\Exception\NotSupportedException; use Nextras\Dbal\ILogger; use Nextras\Dbal\Platforms\IPlatform; @@ -296,7 +297,11 @@ public function convertToPhp(string $value, $nativeType) public function convertStringToSql(string $value): string { assert($this->connection !== null); - return pg_escape_literal($this->connection, $value); + $escaped = pg_escape_literal($this->connection, $value); + if ($escaped === false) { + throw new InvalidStateException(); + } + return $escaped; } @@ -345,7 +350,6 @@ public function convertIdentifierToSql(string $value): string public function convertDateTimeToSql(DateTimeInterface $value): string { $valueTimezone = $value->getTimezone(); - assert($value instanceof DateTime || $value instanceof DateTimeImmutable); assert($valueTimezone !== false); // @phpstan-ignore-line if ($valueTimezone->getName() !== $this->connectionTz->getName()) { if ($value instanceof DateTimeImmutable) { diff --git a/src/Drivers/Pgsql/PgsqlResultAdapter.php b/src/Drivers/Pgsql/PgsqlResultAdapter.php index a4d19c36..398dc2aa 100644 --- a/src/Drivers/Pgsql/PgsqlResultAdapter.php +++ b/src/Drivers/Pgsql/PgsqlResultAdapter.php @@ -85,7 +85,9 @@ public function getTypes(): array for ($i = 0; $i < $count; $i++) { $nativeType = pg_field_type($this->result, $i); - $types[pg_field_name($this->result, $i)] = [ + $name = pg_field_name($this->result, $i); + assert($name !== false); // @phpstan-ignore-line + $types[$name] = [ 0 => self::$types[$nativeType] ?? self::TYPE_AS_IS, 1 => $nativeType, ]; diff --git a/src/Drivers/Sqlsrv/SqlsrvDriver.php b/src/Drivers/Sqlsrv/SqlsrvDriver.php index 6b8bf7d6..ee51348f 100644 --- a/src/Drivers/Sqlsrv/SqlsrvDriver.php +++ b/src/Drivers/Sqlsrv/SqlsrvDriver.php @@ -389,9 +389,6 @@ public function modifyLimitQuery(string $query, ?int $limit, ?int $offset): stri } - /** - * @phpstan-return never - */ private function throwErrors(?string $query = null): void { $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS); @@ -412,7 +409,7 @@ private function throwErrors(?string $query = null): void protected function createException(string $error, int $errorNo, string $sqlState, ?string $query = null): Exception { - if (in_array($sqlState, ['HYT00', '08001', '28000'], true)) { + if (in_array($sqlState, ['HYT00', '08001', '28000'], true) || $errorNo === 4060) { return new ConnectionException($error, $errorNo, $sqlState); } elseif (in_array($errorNo, [547], true)) { diff --git a/tests/cases/integration/platform.mysqli.phpt b/tests/cases/integration/platform.mysqli.phpt index fe701aaf..181387a4 100644 --- a/tests/cases/integration/platform.mysqli.phpt +++ b/tests/cases/integration/platform.mysqli.phpt @@ -7,6 +7,7 @@ namespace NextrasTests\Dbal; +use Nextras\Dbal\Drivers\Mysqli\MysqliDriver; use Tester\Assert; @@ -41,11 +42,21 @@ class PlatformMysqlTest extends IntegrationTestCase $columns = $this->connection->getPlatform()->getColumns('books'); $columns = \array_map(function ($table) { return (array) $table; }, $columns); + $driver = $this->connection->getDriver(); + if ($driver instanceof MysqliDriver) { + $isMariaDb = stripos($driver->getResourceHandle()->server_info, 'MariaDB') !== false; + } else { + $isMariaDb = false; + } + + $isMySQL8 = version_compare($this->connection->getDriver()->getServerVersion(), '8.0.19') >= 0 + && !$isMariaDb; + Assert::same([ 'id' => [ 'name' => 'id', 'type' => 'INT', - 'size' => 11, + 'size' => $isMySQL8 ? null : 11, 'default' => null, 'isPrimary' => true, 'isAutoincrement' => true, @@ -56,7 +67,7 @@ class PlatformMysqlTest extends IntegrationTestCase 'author_id' => [ 'name' => 'author_id', 'type' => 'INT', - 'size' => 11, + 'size' => $isMySQL8 ? null : 11, 'default' => null, 'isPrimary' => false, 'isAutoincrement' => false, @@ -67,7 +78,7 @@ class PlatformMysqlTest extends IntegrationTestCase 'translator_id' => [ 'name' => 'translator_id', 'type' => 'INT', - 'size' => 11, + 'size' => $isMySQL8 ? null : 11, 'default' => null, 'isPrimary' => false, 'isAutoincrement' => false, @@ -89,7 +100,7 @@ class PlatformMysqlTest extends IntegrationTestCase 'publisher_id' => [ 'name' => 'publisher_id', 'type' => 'INT', - 'size' => 11, + 'size' => $isMySQL8 ? null : 11, 'default' => null, 'isPrimary' => false, 'isAutoincrement' => false, @@ -100,7 +111,7 @@ class PlatformMysqlTest extends IntegrationTestCase 'ean_id' => [ 'name' => 'ean_id', 'type' => 'INT', - 'size' => 11, + 'size' => $isMySQL8 ? null : 11, 'default' => null, 'isPrimary' => false, 'isAutoincrement' => false, @@ -119,7 +130,7 @@ class PlatformMysqlTest extends IntegrationTestCase 'id' => [ 'name' => 'id', 'type' => 'INT', - 'size' => 11, + 'size' => $isMySQL8 ? null : 11, 'default' => null, 'isPrimary' => true, 'isAutoincrement' => true, diff --git a/tests/databases.appveyor.ini b/tests/databases.appveyor.ini deleted file mode 100644 index dbcc7c47..00000000 --- a/tests/databases.appveyor.ini +++ /dev/null @@ -1,6 +0,0 @@ -[sqlsrv] -driver = sqlsrv -host = "(local)\SQL2016" -username = sa -password = "Password12!" -database = nextras_dbal_test diff --git a/tests/databases.github.ini b/tests/databases.github.ini new file mode 100644 index 00000000..12201bdc --- /dev/null +++ b/tests/databases.github.ini @@ -0,0 +1,47 @@ +[mysql 5.7] +driver = mysqli +host = "127.0.0.1" +database = nextras_dbal_test +username = root +password = root +port = 3306 + +[mysql 8.0] +driver = mysqli +host = "127.0.0.1" +database = nextras_dbal_test +username = root +password = root +port = 3307 + +[mysql mariadb105] +driver = mysqli +host = "127.0.0.1" +database = nextras_dbal_test +username = root +password = root +port = 3308 + +[pgsql 9.6] +driver = pgsql +host = "127.0.0.1" +database = nextras_dbal_test +username = postgres +password = postgres +port = 5432 + +[pgsql 13] +driver = pgsql +host = "127.0.0.1" +database = nextras_dbal_test +username = postgres +password = postgres +port = 5433 + +[sqlsrv] +driver = sqlsrv +host = "localhost" +username = SA +password = "YourStrong!Passw0rd" +database = nextras_dbal_test +port = 1433 diff --git a/tests/php.unix-sample.ini b/tests/php.unix-sample.ini deleted file mode 100644 index a47909f8..00000000 --- a/tests/php.unix-sample.ini +++ /dev/null @@ -1,3 +0,0 @@ -[PHP] -;extension_dir = "./ext" -;extension=json.so diff --git a/tests/php.win-sample.ini b/tests/php.win-sample.ini deleted file mode 100644 index 2f2a66a7..00000000 --- a/tests/php.win-sample.ini +++ /dev/null @@ -1,5 +0,0 @@ -[PHP] -extension_dir = "./ext" -extension=php_mysqli.dll -extension=php_pgsql.dll -extension=php_sqlsrv_ts.dll diff --git a/tests/run.sh b/tests/run.sh index a5a2c5b0..ee5e83b5 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -1,3 +1,3 @@ #!/bin/sh dir=$(cd `dirname $0` && pwd) -$dir/../vendor/bin/tester -p php -c $dir/php.ini --setup $dir/inc/setup.php $@ +$dir/../vendor/bin/tester -C --setup $dir/inc/setup.php $@