diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index a4755eba..00000000 --- a/.appveyor.yml +++ /dev/null @@ -1,43 +0,0 @@ -build: off -cache: - - c:\php -> .appveyor.yml - - '%LOCALAPPDATA%\Composer\files -> .appveyor.yml' - -clone_folder: c:\projects\orm - -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.3.9-Win32-VC15-x64.zip https://windows.php.net/downloads/releases/archives/php-7.3.9-Win32-VC15-x64.zip - - IF %PHP%==1 7z x php-7.3.9-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.6.1/Windows-7.3.zip - - IF %PHP%==1 7z x Windows-7.3.zip >nul - - IF %PHP%==1 copy Windows-7.3\x64\php_sqlsrv_73_ts.dll ext\php_sqlsrv_ts.dll - - IF %PHP%==1 del /Q *.zip - - cd c:\projects\orm - - # Install Nette Tester - - appveyor DownloadFile https://getcomposer.org/composer.phar - - php composer.phar install --prefer-dist --no-interaction --no-progress - - # Create sections.ini, config.mssql.neon & php.ini - - copy tests\sections.appveyor.ini tests\sections.ini - - copy tests\config.array.sample.neon tests\config.array.neon - - copy tests\config.mssql.sample.neon tests\config.mssql.neon - - copy tests\php-win.ini tests\php.ini - -test_script: - - sqlcmd -S ".\SQL2016" -U sa -P Password12! -Q "CREATE DATABASE nextras_orm_test" -d "master" - - vendor\bin\tester -p php -c tests\php.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..de1cedfc --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,232 @@ +name: "Build" + +on: + pull_request: + paths-ignore: + - doc/** + push: + paths-ignore: + - doc/** + branches: + - main + - v*.* + +env: + php-extensions: mbstring, intl, mysqli, pgsql, sqlsrv-5.10.0-beta1 + php-extensions-key: v2 + php-tools: "composer:v2, pecl" + +jobs: + phpstan: + name: PHPStan + + runs-on: ubuntu-latest + + strategy: + matrix: + php-version: [ '7.4', '8.0' ] + + 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' ] + deps: [ 'lowest', 'newest' ] + exclude: + - php-version: '7.2' + deps: lowest + - php-version: '7.3' + deps: lowest + - php-version: '7.4' + deps: lowest + - php-version: '8.0' + deps: lowest + + runs-on: ubuntu-latest + + services: + mysql57: + image: mysql:5.7 + env: + MYSQL_DATABASE: nextras_orm_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_orm_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_orm_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_orm_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_orm_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_orm_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 + if: matrix.deps == 'newest' + run: composer update --prefer-dist --no-interaction --no-progress --no-suggest + + - name: Install lowest dependencies + if: matrix.deps == 'lowest' + run: composer update --prefer-dist --no-interaction --no-progress --no-suggest --prefer-lowest --prefer-stable + + - name: Init config + run: | + cp ./tests/sections.sample.ini ./tests/sections.ini + cp ./tests/config.array.sample.neon ./tests/config.array.neon + cp ./tests/config.mssql.sample.neon ./tests/config.mssql.neon + cp ./tests/config.mysql.sample.neon ./tests/config.mysql.neon + cp ./tests/config.pgsql.sample.neon ./tests/config.pgsql.neon + + - 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 b9dfa31c..bd9a89db 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 - tests/cases @@ -15,6 +15,7 @@ parameters: Tester\Assert: - fail treatPhpDocTypesAsCertain: false + reportUnmatchedIgnoredErrors: false ignoreErrors: - '#Call to static method Tester\\Assert::type\(\).+will always evaluate to true\.#' diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index bb979130..00000000 --- a/.travis.yml +++ /dev/null @@ -1,59 +0,0 @@ -language: php - -php: - - 7.1 - - 7.2 - - 7.3 - - 7.4 - -services: - - postgresql - - mysql - -matrix: - fast_finish: true - -cache: - directories: - - $HOME/.composer/cache - -before_install: - - phpenv config-rm xdebug.ini || true - - # Create php.ini & sections.ini - - cp ./tests/config.array.sample.neon ./tests/config.array.neon && cp ./tests/config.pgsql.sample.neon ./tests/config.pgsql.neon && cp ./tests/config.mysql.sample.neon ./tests/config.mysql.neon - - cp ./tests/php-unix.ini ./tests/php.ini - - cp ./tests/sections.sample.ini ./tests/sections.ini - - # Create MySQL & Postgre database - - psql -c 'CREATE DATABASE nextras_orm_test' -U postgres - - mysql -e 'CREATE DATABASE nextras_orm_test;' - - mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql - -install: - - travis_retry composer update --no-interaction --prefer-dist --no-progress - -jobs: - 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 0e2d084a..2f80da3a 100644 --- a/composer.json +++ b/composer.json @@ -36,13 +36,13 @@ "marc-mabe/php-enum": "~3.0", "mockery/mockery": "~1.2", "phpstan/extension-installer": "1.0.5", - "phpstan/phpstan": "0.12.42", - "phpstan/phpstan-deprecation-rules": "0.12.5", - "phpstan/phpstan-nette": "0.12.9", - "phpstan/phpstan-mockery": "0.12.7", - "phpstan/phpstan-strict-rules": "0.12.5", - "nextras/orm-phpstan": "dev-master", - "marc-mabe/php-enum-phpstan": "dev-master", + "phpstan/phpstan": "1.1.2", + "phpstan/phpstan-deprecation-rules": "1.0.0", + "phpstan/phpstan-nette": "1.0.0", + "phpstan/phpstan-mockery": "1.0.0", + "phpstan/phpstan-strict-rules": "1.0.0", + "nextras/orm-phpstan": "1.0.0", + "marc-mabe/php-enum-phpstan": "2.0.0", "tracy/tracy": "~2.3" }, "autoload": { diff --git a/src/Bridges/NetteDI/OrmExtension.php b/src/Bridges/NetteDI/OrmExtension.php index 86b24be2..84e24d2f 100644 --- a/src/Bridges/NetteDI/OrmExtension.php +++ b/src/Bridges/NetteDI/OrmExtension.php @@ -40,10 +40,11 @@ public function loadConfiguration() $this->modelClass = $config['model']; $repositoryFinderClass = $config['repositoryFinder']; - $this->repositoryFinder = new $repositoryFinderClass($this->modelClass, $this->builder, $this); - if (!$this->repositoryFinder instanceof IRepositoryFinder) { + $repositoryFinder = new $repositoryFinderClass($this->modelClass, $this->builder, $this); + if (!$repositoryFinder instanceof IRepositoryFinder) { throw new InvalidStateException('Repository finder does not implement Nextras\Orm\Bridges\NetteDI\IRepositoryFinder interface.'); } + $this->repositoryFinder = $repositoryFinder; $repositories = $this->repositoryFinder->loadConfiguration(); diff --git a/src/Collection/ArrayCollection.php b/src/Collection/ArrayCollection.php index b1d30b52..f6426493 100644 --- a/src/Collection/ArrayCollection.php +++ b/src/Collection/ArrayCollection.php @@ -22,7 +22,7 @@ class ArrayCollection implements ICollection { /** * @var callable[] - * @phpstan-var list + * @phpstan-var list $entities): void> */ public $onEntityFetch = []; diff --git a/src/Collection/DbalCollection.php b/src/Collection/DbalCollection.php index 02c58c45..30a8ed74 100644 --- a/src/Collection/DbalCollection.php +++ b/src/Collection/DbalCollection.php @@ -21,7 +21,7 @@ class DbalCollection implements ICollection { /** * @var callable[] - * @phpstan-var list + * @phpstan-var list $entities): void> */ public $onEntityFetch = []; diff --git a/src/Collection/Helpers/ArrayCollectionHelper.php b/src/Collection/Helpers/ArrayCollectionHelper.php index b04cf956..24976e1f 100644 --- a/src/Collection/Helpers/ArrayCollectionHelper.php +++ b/src/Collection/Helpers/ArrayCollectionHelper.php @@ -217,7 +217,6 @@ public function __toString() do { /** @var (array{IEntity,array,EntityMetadata}) $shift */ $shift = array_shift($stack); - assert($shift !== null); $value = $shift[0]; $tokens = $shift[1]; $entityMeta = $shift[2]; diff --git a/src/Collection/Helpers/DbalExpressionResult.php b/src/Collection/Helpers/DbalExpressionResult.php index e8c1c9e5..d5a96739 100644 --- a/src/Collection/Helpers/DbalExpressionResult.php +++ b/src/Collection/Helpers/DbalExpressionResult.php @@ -69,6 +69,6 @@ public function append(string $expression, ...$args): DbalExpressionResult { array_unshift($args, $this->args); array_unshift($args, "%ex $expression"); - return new DbalExpressionResult($args, $this->isHavingClause); + return new DbalExpressionResult($args, $this->isHavingClause); // @phpstan-ignore-line } } diff --git a/src/Collection/Helpers/FetchPairsHelper.php b/src/Collection/Helpers/FetchPairsHelper.php index e0c5d5ee..e07b28f5 100644 --- a/src/Collection/Helpers/FetchPairsHelper.php +++ b/src/Collection/Helpers/FetchPairsHelper.php @@ -87,7 +87,6 @@ private static function getProperty(IEntity $row, array $chain) throw new InvalidStateException("Part '$lastPropertyName' of the chain expression does not select an IEntity nor an IEmbeddable."); } $lastPropertyName = $propertyName; - // @phpstan-ignore-next-line Bug in while & array_shift https://github.com/phpstan/phpstan/issues/2611 $result = $result->getValue($propertyName); } return $result; diff --git a/src/Collection/ICollection.php b/src/Collection/ICollection.php index 6f56165b..348ec78c 100644 --- a/src/Collection/ICollection.php +++ b/src/Collection/ICollection.php @@ -182,6 +182,7 @@ public function fetchPairs(?string $key = null, ?string $value = null): array; /** * @return Iterator */ + #[\ReturnTypeWillChange] public function getIterator(); diff --git a/src/Entity/Reflection/MetadataParser.php b/src/Entity/Reflection/MetadataParser.php index 8b6fe3a9..da5ed789 100644 --- a/src/Entity/Reflection/MetadataParser.php +++ b/src/Entity/Reflection/MetadataParser.php @@ -134,7 +134,8 @@ protected function loadProperties(?array &$fileDependencies): void foreach (array_reverse($classTree) as $class) { if (!isset($this->classPropertiesCache[$class])) { - foreach (class_uses($class) as $traitName) { + $traits = class_uses($class); + foreach ($traits !== false ? $traits : [] as $traitName) { assert(trait_exists($traitName)); $reflectionTrait = new ReflectionClass($traitName); $fileDependencies[] = $reflectionTrait->getFileName(); @@ -148,7 +149,8 @@ protected function loadProperties(?array &$fileDependencies): void $this->classPropertiesCache[$class] = $this->parseAnnotations($reflection, $methods); } - foreach (class_uses($class) as $traitName) { + $traits = class_uses($class); + foreach ($traits !== false ? $traits : [] as $traitName) { foreach ($this->classPropertiesCache[$traitName] as $name => $property) { $this->metadata->setProperty($name, $property); } @@ -329,6 +331,7 @@ protected function parseOneHasOneModifier(PropertyMetadata $property, array &$ar $this->processRelationshipIsMain($property, $args); $this->processRelationshipEntityProperty($property, $args); $this->processRelationshipCascade($property, $args); + assert($property->relationship !== null); $property->isVirtual = !$property->relationship->isMain; } @@ -415,7 +418,7 @@ protected function parseWrapperModifier(PropertyMetadata $property, array &$args throw new InvalidModifierDefinitionException("Class '$className' in {wrapper} for {$this->currentReflection->name}::\${$property->name} property does not exist."); } $implements = class_implements($className); - if (!isset($implements[IProperty::class])) { + if ($implements !== false && !isset($implements[IProperty::class])) { throw new InvalidModifierDefinitionException("Class '$className' in {wrapper} for {$this->currentReflection->name}::\${$property->name} property does not implement Nextras\\Orm\\Entity\\IProperty interface."); } $property->wrapper = $className; diff --git a/src/Entity/Reflection/PropertyMetadata.php b/src/Entity/Reflection/PropertyMetadata.php index fe6b8d4c..6fd9cf2c 100644 --- a/src/Entity/Reflection/PropertyMetadata.php +++ b/src/Entity/Reflection/PropertyMetadata.php @@ -72,6 +72,7 @@ public function getWrapperPrototype(): IProperty if ($this->wrapper === null) { throw new InvalidStateException(); } + /** @var class-string $class */ $class = $this->wrapper; $this->wrapperPrototype = new $class($this); } diff --git a/src/Mapper/Dbal/Conventions/Conventions.php b/src/Mapper/Dbal/Conventions/Conventions.php index 07c88fb4..1eae2f9c 100644 --- a/src/Mapper/Dbal/Conventions/Conventions.php +++ b/src/Mapper/Dbal/Conventions/Conventions.php @@ -210,7 +210,7 @@ public function convertStorageToEntity(array $in): array } if (stripos($newKey, '->') !== false) { - $ref = &Arrays::getRef($out, explode('->', $newKey)); + $ref = &Arrays::getRef($out, explode('->', $newKey)); // @phpstan-ignore-line $ref = $val; } else { $out[$newKey] = $val; @@ -362,7 +362,8 @@ protected function getDefaultMappings(): array /** @phpstan-var list}> $toProcess */ $toProcess = [[$this->entityMetadata, []]]; - while (([$metadata, $tokens] = array_shift($toProcess)) !== null) { + while (($entry = array_shift($toProcess)) !== null) { + [$metadata, $tokens] = $entry; foreach ($metadata->getProperties() as $property) { if ($property->wrapper !== EmbeddableContainer::class) { continue; diff --git a/src/Mapper/Dbal/RelationshipMapperManyHasMany.php b/src/Mapper/Dbal/RelationshipMapperManyHasMany.php index a0e5f528..2eee7123 100644 --- a/src/Mapper/Dbal/RelationshipMapperManyHasMany.php +++ b/src/Mapper/Dbal/RelationshipMapperManyHasMany.php @@ -157,8 +157,8 @@ private function fetchByTwoPassStrategy(QueryBuilder $builder, array $values): M } $values = []; - foreach ($result as $row) { - $values[$row->{$this->primaryKeyTo}] = null; + foreach ($result as $groupingRow) { + $values[$groupingRow->{$this->primaryKeyTo}] = null; } if (count($values) === 0) { diff --git a/src/Mapper/Memory/ArrayMapper.php b/src/Mapper/Memory/ArrayMapper.php index 2efdf30e..499a01bf 100644 --- a/src/Mapper/Memory/ArrayMapper.php +++ b/src/Mapper/Memory/ArrayMapper.php @@ -280,12 +280,11 @@ protected function entityToArray(IEntity $entity): array */ protected function readEntityData(): array { - // @phpstan-ignore-next-line https://github.com/phpstan/phpstan/issues/3357 - [$data, $relationshipData] = $this->readData() ?: [[], []]; + $stored = $this->readData(); if ($this->relationshipData === []) { - $this->relationshipData = $relationshipData; + $this->relationshipData = $stored[1] ?? []; } - return $data; + return $stored[0] ?? []; } diff --git a/src/Model/MetadataStorage.php b/src/Model/MetadataStorage.php index c5031593..72924cbd 100644 --- a/src/Model/MetadataStorage.php +++ b/src/Model/MetadataStorage.php @@ -24,13 +24,13 @@ class MetadataStorage public static function get(string $className): EntityMetadata { - if (!isset(static::$metadata[$className])) { - if (static::$metadata === []) { + if (!isset(self::$metadata[$className])) { + if (self::$metadata === []) { throw new InvalidStateException("MetadataStorage::get() called too early. You have to instantiate your model first."); } throw new InvalidArgumentException("Entity metadata for '{$className}' does not exist."); } - return static::$metadata[$className]; + return self::$metadata[$className]; } @@ -85,6 +85,6 @@ function (&$dp) use ($entityClassesMap, $metadataParserFactory, $repositoryLoade } } - static::$metadata += $metadata; + self::$metadata += $metadata; } } diff --git a/src/Repository/RemovalHelper.php b/src/Repository/RemovalHelper.php index 478cb243..cf0d70b1 100644 --- a/src/Repository/RemovalHelper.php +++ b/src/Repository/RemovalHelper.php @@ -41,7 +41,7 @@ public static function getCascadeQueueAndSetNulls( [$pre, $post, $nulls] = static::getRelationships($entity); $prePersist = []; - static::setNulls($entity, $nulls, $model, $prePersist, $queueRemove); + self::setNulls($entity, $nulls, $model, $prePersist, $queueRemove); if (!$withCascade) { $queueRemove[$entityHash] = $entity; diff --git a/src/Repository/Repository.php b/src/Repository/Repository.php index fa021816..37aa0cda 100644 --- a/src/Repository/Repository.php +++ b/src/Repository/Repository.php @@ -330,7 +330,9 @@ protected function createCollectionFunction(string $name) } if (isset($knownFunctions[$name])) { - return new $name(); + /** @var IQueryBuilderFunction|IArrayFunction $function */ + $function = new $name(); + return $function; } else { throw new NotImplementedException('Override ' . get_class($this) . '::createCollectionFunction() to return an instance of ' . $name . ' collection function.'); } diff --git a/tests/.gitignore b/tests/.gitignore index 2f4c2d53..45563b19 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,7 +1,6 @@ output /tmp /test.log -/php.ini /sections.ini /config.array.neon /config.mysql.neon diff --git a/tests/cases/integration/Relationships/relationships.oneHasMany.phpt b/tests/cases/integration/Relationships/relationships.oneHasMany.phpt index 5239cfda..83a254ba 100644 --- a/tests/cases/integration/Relationships/relationships.oneHasMany.phpt +++ b/tests/cases/integration/Relationships/relationships.oneHasMany.phpt @@ -256,7 +256,7 @@ class RelationshipOneHasManyTest extends DataTestCase $mapper = $this->orm->tagFollowers->getMapper(); Assert::type(DbalMapper::class, $mapper); $mapper->rollback(); - \assert(isset($tagFollower)); // @phpstan-ignore-line + \assert(isset($tagFollower)); Assert::false($tagFollower->isPersisted()); } } diff --git a/tests/cases/unit/Entity/AbstractEntity.getters_setters.phpt b/tests/cases/unit/Entity/AbstractEntity.getters_setters.phpt index 7ac37fd8..08c35d11 100644 --- a/tests/cases/unit/Entity/AbstractEntity.getters_setters.phpt +++ b/tests/cases/unit/Entity/AbstractEntity.getters_setters.phpt @@ -18,7 +18,10 @@ use Tester\Assert; $dic = require_once __DIR__ . '/../../../bootstrap.php'; -abstract class GetterSetterTestEntity extends AbstractEntity +/** + * @property string|null $isMain + */ +class GetterSetterTestEntity extends AbstractEntity { public function setMetadata(EntityMetadata $metadata): void { @@ -26,9 +29,20 @@ abstract class GetterSetterTestEntity extends AbstractEntity } - // @phpstan-ignore-next-line protected function createMetadata(): EntityMetadata { + $propertyMetadata = Mockery::mock(PropertyMetadata::class); + $propertyMetadata->name = 'isMain'; + $propertyMetadata->hasSetter = 'setterIsMain'; + $propertyMetadata->hasGetter = 'getterIsMain'; + $propertyMetadata->isNullable = true; + $propertyMetadata->shouldReceive('isValid')->with(false)->twice()->andReturn(true); + $propertyMetadata->shouldReceive('isValid')->with(true)->once()->andReturn(true); + + $metadata = Mockery::mock(EntityMetadata::class); + $metadata->shouldReceive('getProperty')->with('isMain')->andReturn($propertyMetadata); + $metadata->shouldReceive('getProperties')->andReturn(['isMain' => $propertyMetadata]); + return $metadata; } @@ -49,31 +63,14 @@ class AbstractEntityGettersSettersTest extends TestCase { public function testBasics(): void { - $propertyMetadata = Mockery::mock(PropertyMetadata::class); - $propertyMetadata->name = 'isMain'; - $propertyMetadata->hasSetter = 'setterIsMain'; - $propertyMetadata->hasGetter = 'getterIsMain'; - $propertyMetadata->isNullable = true; - $propertyMetadata->shouldReceive('isValid')->with(false)->twice()->andReturn(true); - $propertyMetadata->shouldReceive('isValid')->with(true)->once()->andReturn(true); - - $metadata = Mockery::mock(EntityMetadata::class); - $metadata->shouldReceive('getProperty')->with('isMain')->andReturn($propertyMetadata); - $metadata->shouldReceive('getProperties')->andReturn(['isMain' => $propertyMetadata]); - - /** @var GetterSetterTestEntity $entity */ - $entity = Mockery::mock(GetterSetterTestEntity::class)->makePartial(); - $entity->setMetadata($metadata); + $entity = new GetterSetterTestEntity(); - // Property is metadata only @phpstan-ignore-next-line $entity->setValue('isMain', 'yes'); Assert::null($entity->getValue('isMain')); - // Property is metadata only @phpstan-ignore-next-line $entity->setValue('isMain', null); Assert::null($entity->getValue('isMain')); - // Property is metadata only @phpstan-ignore-next-line $entity->setValue('isMain', 'Yes'); Assert::same('Yes', $entity->getValue('isMain')); diff --git a/tests/config.mssql.sample.neon b/tests/config.mssql.sample.neon index 1710d851..599da18a 100644 --- a/tests/config.mssql.sample.neon +++ b/tests/config.mssql.sample.neon @@ -4,9 +4,10 @@ extensions: nextras.dbal: driver: sqlsrv - host: "(local)\\SQL2016" - username: sa - password: "Password12!" + host: localhost + port: 1433 + username: SA + password: "YourStrong!Passw0rd" database: nextras_orm_test nextras.orm: diff --git a/tests/config.mysql.sample.neon b/tests/config.mysql.sample.neon index 976581a4..7a326133 100644 --- a/tests/config.mysql.sample.neon +++ b/tests/config.mysql.sample.neon @@ -7,7 +7,7 @@ nextras.dbal: host: 127.0.0.1 database: nextras_orm_test username: root - password: + password: root connectionTz: UTC nextras.orm: diff --git a/tests/config.pgsql.sample.neon b/tests/config.pgsql.sample.neon index 0912272a..288066ee 100644 --- a/tests/config.pgsql.sample.neon +++ b/tests/config.pgsql.sample.neon @@ -4,10 +4,10 @@ extensions: nextras.dbal: driver: pgsql - host: localhost + host: "127.0.0.1" database: nextras_orm_test username: postgres - password: + password: postgres connectionTz: UTC nextras.orm: diff --git a/tests/inc/Helper.php b/tests/inc/Helper.php index 898a7b94..ac2e95ec 100644 --- a/tests/inc/Helper.php +++ b/tests/inc/Helper.php @@ -21,9 +21,6 @@ public static function check(): void if (!is_file(__DIR__ . '/../sections.ini')) { throw new InvalidStateException("Missing 'tests/sections.ini' configuration file."); } - if (!is_file(__DIR__ . '/../php.ini')) { - throw new InvalidStateException("Missing 'tests/php.ini' configuration file."); - } } @@ -35,6 +32,7 @@ public static function getSection(): string } $tmp = preg_filter('#--dataprovider=(.*)#Ai', '$1', $_SERVER['argv']); + assert(is_array($tmp)); [$query] = explode('|', (string) reset($tmp), 2); return $query !== '' ? $query : self::SECTION_ARRAY; diff --git a/tests/inc/phpstan/AssertTypeSpecifierExtension.php b/tests/inc/phpstan/AssertTypeSpecifierExtension.php index b8b48e07..d500d1d3 100644 --- a/tests/inc/phpstan/AssertTypeSpecifierExtension.php +++ b/tests/inc/phpstan/AssertTypeSpecifierExtension.php @@ -58,12 +58,12 @@ public function specifyTypes( $name = strtolower($staticMethodReflection->getName()); if ($name === 'notnull') { $expression = new \PhpParser\Node\Expr\BinaryOp\NotIdentical( - $node->args[0]->value, + $node->getArgs()[0]->value, new \PhpParser\Node\Expr\ConstFetch(new \PhpParser\Node\Name('null')) ); } elseif ($name === 'type') { - $expr = $node->args[1]; - $class = $node->args[0]; + $expr = $node->getArgs()[1]; + $class = $node->getArgs()[0]; $classType = $scope->getType($class->value); if (!$classType instanceof ConstantStringType) { diff --git a/tests/php-unix.ini b/tests/php-unix.ini deleted file mode 100644 index d29e9c36..00000000 --- a/tests/php-unix.ini +++ /dev/null @@ -1,8 +0,0 @@ -[PHP] -memory_limit = 512M - -;extension_dir = "./ext" -;extension=json.so - -[Zend] -;zend_extension="./ext/zend_extension" diff --git a/tests/php-win.ini b/tests/php-win.ini deleted file mode 100644 index 2f2a66a7..00000000 --- a/tests/php-win.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 aa6dd803..7a0f452b 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" $@ diff --git a/tests/sections.appveyor.ini b/tests/sections.appveyor.ini deleted file mode 100644 index e1c6d246..00000000 --- a/tests/sections.appveyor.ini +++ /dev/null @@ -1 +0,0 @@ -[mssql] diff --git a/tests/sections.sample.ini b/tests/sections.sample.ini index bed5357b..d79bd6c7 100644 --- a/tests/sections.sample.ini +++ b/tests/sections.sample.ini @@ -3,3 +3,5 @@ [mysql] [pgsql] + +[mssql]