From 597ba27c7f51e3f02b6bd089885b26a2a9afcbc5 Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Sun, 2 Aug 2026 20:39:30 +0100 Subject: [PATCH 01/14] Add GitHub Actions CI --- .github/workflows/ci.yml | 171 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..e46868f0 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,171 @@ +name: CI + +on: + push: + pull_request: + branches: [ master ] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + PHP_VERSION: '8.3' + +jobs: + tests: + name: PHPUnit + runs-on: ubuntu-latest + + # These mirror docker-compose.yml. Beanstalkd isn't started because + # nothing in tests/ currently dispatches through Messenger; add a + # service for it here if that changes. + services: + mariadb: + image: mariadb:11 + env: + MYSQL_ROOT_PASSWORD: secret + MYSQL_DATABASE: omm_test + MYSQL_USER: omm_test + MYSQL_PASSWORD: omm_test + ports: + - 3306:3306 + options: >- + --health-cmd="mysqladmin ping -h 127.0.0.1 -u root -psecret" + --health-interval=10s + --health-timeout=5s + --health-retries=10 + + elasticsearch: + image: docker.elastic.co/elasticsearch/elasticsearch:7.17.22 + env: + discovery.type: single-node + xpack.security.enabled: "false" + cluster.routing.allocation.disk.threshold_enabled: "false" + ports: + - 9200:9200 + options: >- + --health-cmd="curl -sf http://127.0.0.1:9200/_cluster/health" + --health-interval=10s + --health-timeout=5s + --health-retries=15 + + redis: + image: redis:8.4-alpine + ports: + - 6379:6379 + options: >- + --health-cmd="redis-cli ping" + --health-interval=10s + --health-timeout=5s + --health-retries=10 + + env: + APP_ENV: test + DATABASE_URL: mysql://omm_test:omm_test@127.0.0.1:3306/omm_test + ELASTICSEARCH_URL: http://127.0.0.1:9200/ + REDIS_URL: redis://127.0.0.1:6379 + # Ubuntu's apt package puts it here, not the /usr/local/bin the + # phpunit.dist.xml default assumes (that's a Docker-image path). + TEST_EXIFTOOL_PATH: /usr/bin/exiftool + + steps: + - uses: actions/checkout@v4 + + - name: Install exiftool + run: sudo apt-get update && sudo apt-get install -y exiftool + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ env.PHP_VERSION }} + extensions: ctype, iconv, pdo, pdo_mysql, mysqli, zip, apcu, bcmath, intl, gd + coverage: none + tools: composer:v2 + + - 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-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress --no-interaction + + # Actions blocks these steps until the health checks above report + # healthy, so unlike docker/testentrypoint.sh's wait-for-it.sh dance, + # there's nothing to poll for manually here. + + - name: Create test database schema + run: php bin/console doctrine:migrations:migrate --env=test --no-interaction + + - name: Create Elasticsearch test index + run: php bin/console fos:elastica:reset --env=test --no-interaction + + - name: Run test suite + run: vendor/bin/phpunit --testdox + + phpstan: + name: PHPStan + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ env.PHP_VERSION }} + coverage: none + tools: composer:v2 + + - 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-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress --no-interaction + + - name: Run PHPStan + run: vendor/bin/phpstan analyse --no-progress + + rector: + name: Rector (dry run) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ env.PHP_VERSION }} + coverage: none + tools: composer:v2 + + - 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-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress --no-interaction + + - name: Check for outstanding Rector refactors + run: vendor/bin/rector process --dry-run From 69082b0ea9574175d80452065ca98f7fb2bcff7e Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Sun, 2 Aug 2026 21:04:33 +0100 Subject: [PATCH 02/14] PHPStan fixes --- src/Command/AdminCreateAdminCommand.php | 1 + src/Command/DeleteAllImagesCommand.php | 1 + src/Command/DeleteAllWandersCommand.php | 1 + src/Command/DeleteImagesNewerThanCommand.php | 1 + src/Command/UpdateImagesFromExifCommand.php | 1 + src/Command/WanderUpdateFromGpxCommand.php | 1 + src/Service/ProblemService.php | 2 +- 7 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Command/AdminCreateAdminCommand.php b/src/Command/AdminCreateAdminCommand.php index 1c5d12df..c2c7b662 100644 --- a/src/Command/AdminCreateAdminCommand.php +++ b/src/Command/AdminCreateAdminCommand.php @@ -48,6 +48,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $question = new Question("Please enter a password for new user '{$username}': ", 'AcmeDemoBundle'); $question->setHidden(true); + /** @var \Symfony\Component\Console\Helper\QuestionHelper $helper */ $helper = $this->getHelper('question'); $password = $helper->ask($input, $output, $question); diff --git a/src/Command/DeleteAllImagesCommand.php b/src/Command/DeleteAllImagesCommand.php index e99652e7..e7c3bf46 100644 --- a/src/Command/DeleteAllImagesCommand.php +++ b/src/Command/DeleteAllImagesCommand.php @@ -23,6 +23,7 @@ public function __construct(private readonly ImageRepository $imageRepository, p protected function execute(InputInterface $input, OutputInterface $output): int { + /** @var \Symfony\Component\Console\Helper\QuestionHelper $helper */ $helper = $this->getHelper('question'); $question = new ConfirmationQuestion('Are you sure you want to delete ALL images? ', false); if (!$helper->ask($input, $output, $question)) { diff --git a/src/Command/DeleteAllWandersCommand.php b/src/Command/DeleteAllWandersCommand.php index 98f864c7..ee0d5c84 100644 --- a/src/Command/DeleteAllWandersCommand.php +++ b/src/Command/DeleteAllWandersCommand.php @@ -23,6 +23,7 @@ public function __construct(private readonly WanderRepository $wanderRepository, protected function execute(InputInterface $input, OutputInterface $output): int { + /** @var \Symfony\Component\Console\Helper\QuestionHelper $helper */ $helper = $this->getHelper('question'); $question = new ConfirmationQuestion('Are you sure you want to delete ALL wanders? ', false); if (!$helper->ask($input, $output, $question)) { diff --git a/src/Command/DeleteImagesNewerThanCommand.php b/src/Command/DeleteImagesNewerThanCommand.php index d6a7e528..f6a247ab 100644 --- a/src/Command/DeleteImagesNewerThanCommand.php +++ b/src/Command/DeleteImagesNewerThanCommand.php @@ -30,6 +30,7 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { + /** @var \Symfony\Component\Console\Helper\QuestionHelper $helper */ $helper = $this->getHelper('question'); $id = filter_var($input->getArgument('id'), FILTER_VALIDATE_INT, ['min_range' => 0]); if ($id === false) { diff --git a/src/Command/UpdateImagesFromExifCommand.php b/src/Command/UpdateImagesFromExifCommand.php index 9cd22ecd..33a50808 100644 --- a/src/Command/UpdateImagesFromExifCommand.php +++ b/src/Command/UpdateImagesFromExifCommand.php @@ -33,6 +33,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int { $updateWanders = $input->getOption('update-wanders'); + /** @var \Symfony\Component\Console\Helper\QuestionHelper $helper */ $helper = $this->getHelper('question'); if ($updateWanders) { diff --git a/src/Command/WanderUpdateFromGpxCommand.php b/src/Command/WanderUpdateFromGpxCommand.php index d6580510..469f1b32 100644 --- a/src/Command/WanderUpdateFromGpxCommand.php +++ b/src/Command/WanderUpdateFromGpxCommand.php @@ -25,6 +25,7 @@ public function __construct(private readonly WanderRepository $wanderRepository, protected function execute(InputInterface $input, OutputInterface $output): int { + /** @var \Symfony\Component\Console\Helper\QuestionHelper $helper */ $helper = $this->getHelper('question'); $question = new ConfirmationQuestion('Are you sure you want to update all wanders based on their GPX track? ', false); if (!$helper->ask($input, $output, $question)) { diff --git a/src/Service/ProblemService.php b/src/Service/ProblemService.php index e519e0e0..1cf1355c 100644 --- a/src/Service/ProblemService.php +++ b/src/Service/ProblemService.php @@ -14,7 +14,7 @@ class ProblemService { - public function __construct(private readonly problemRepository $problemRepository, private readonly WanderRepository $wanderRepository, private readonly ImageRepository $imageRepository, private readonly RouterInterface $router, private readonly MarkdownService $markdownService, private readonly EntityManagerInterface $entityManager, private readonly SpellingService $spellingService) + public function __construct(private readonly ProblemRepository $problemRepository, private readonly WanderRepository $wanderRepository, private readonly ImageRepository $imageRepository, private readonly RouterInterface $router, private readonly MarkdownService $markdownService, private readonly EntityManagerInterface $entityManager, private readonly SpellingService $spellingService) { } From 6f2bbb2806046ac8e0073a21fe8ab735c8e5a22f Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Sun, 2 Aug 2026 21:47:04 +0100 Subject: [PATCH 03/14] Fix up the more important PHPStan warnings. --- tests/ImageTagTest.php | 9 +++++++-- tests/MarkdownServiceTest.php | 4 +--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/ImageTagTest.php b/tests/ImageTagTest.php index 0248c342..545d266f 100644 --- a/tests/ImageTagTest.php +++ b/tests/ImageTagTest.php @@ -10,10 +10,11 @@ use App\Service\ImageService; use Doctrine\Common\Collections\ArrayCollection; use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\MockObject; +use PHPUnit\Framework\MockObject\MockObject; use Beelab\TagBundle\Tag\TagInterface; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Component\Translation\Util\ArrayConverter; +use Doctrine\ORM\EntityManagerInterface; final class ImageTagTest extends KernelTestCase { @@ -325,10 +326,14 @@ public function testSaveTagsAvoidsDuplication(): void // The first tag of image2 should be "two"... $image2tagTwo = $image2->getTags()->current(); + assert($image2tagTwo instanceof Tag); $this->assertEquals('two', $image2tagTwo->getName(), "Unexpected tag name found in image2"); // ...but it should also be the *same* tag as was used // for "two" in image1, so the same database id should // exist in image2's tags. - $this->assertTrue($image1->getTags()->exists(fn($k, $v) => $v->getId() === $image2tagTwo->getId())); + $this->assertTrue($image1->getTags()->exists(function ($k, $v) use ($image2tagTwo) { + assert($v instanceof Tag); + return $v->getId() === $image2tagTwo->getId(); + })); } } diff --git a/tests/MarkdownServiceTest.php b/tests/MarkdownServiceTest.php index 4037082d..1087aa2a 100644 --- a/tests/MarkdownServiceTest.php +++ b/tests/MarkdownServiceTest.php @@ -33,11 +33,9 @@ protected function setUp(): void $container = self::getContainer(); $cache = $container->get(TagAwareCacheInterface::class); - $logger = $this->createStub(LoggerInterface::class); $this->markdownService = new MarkdownService( - $cache, - $logger + $cache ); } From adf4e723dbdf5c82cfc2cd74579936c2ce9a3d9b Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Sun, 2 Aug 2026 21:47:31 +0100 Subject: [PATCH 04/14] Add .claude to git ignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index eb8807a2..19100707 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,5 @@ yarn-error.log /phpunit.xml /.phpunit.cache/ ###< phpunit/phpunit ### + +/.claude/settings.local.json From 8ff42c0686f8996cc053ccdcd0b855e57ad6fe99 Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Sun, 2 Aug 2026 21:50:43 +0100 Subject: [PATCH 05/14] Baseline PHPStan --- phpstan-baseline.neon | 805 ++++++++++++++++++++++++++++++++++++++++++ phpstan.neon.dist | 3 + 2 files changed, 808 insertions(+) create mode 100644 phpstan-baseline.neon diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 00000000..11e04146 --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,805 @@ +parameters: + ignoreErrors: + - + message: '#^Deprecated in PHP 8\.4\: Parameter \#4 \$format \(string\) is implicitly nullable via default value null\.$#' + identifier: parameter.implicitlyNullable + count: 2 + path: src/Controller/Api/ApiController.php + + - + message: '#^Method App\\DataFixtures\\NeighbourhoodFixtures\:\:getPolygonRings\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: src/DataFixtures/NeighbourhoodFixtures.php + + - + message: '#^Negated boolean expression is always false\.$#' + identifier: booleanNot.alwaysFalse + count: 2 + path: src/Entity/Image.php + + - + message: '#^Parameter \#2 \$array of function implode expects array\, array\ given\.$#' + identifier: argument.type + count: 1 + path: src/Entity/Image.php + + - + message: '#^Property App\\Entity\\Image\:\:\$id \(int\|null\) is never assigned int so it can be removed from the property type\.$#' + identifier: property.unusedType + count: 1 + path: src/Entity/Image.php + + - + message: '#^Property App\\Entity\\Image\:\:\$latlng \(array\\|null\) is never assigned array\ so it can be removed from the property type\.$#' + identifier: property.unusedType + count: 1 + path: src/Entity/Image.php + + - + message: '#^Method App\\Entity\\Neighbourhood\:\:getBoundingPolygon\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: src/Entity/Neighbourhood.php + + - + message: '#^Method App\\Entity\\Neighbourhood\:\:setBoundingPolygon\(\) has parameter \$boundingPolygon with no type specified\.$#' + identifier: missingType.parameter + count: 1 + path: src/Entity/Neighbourhood.php + + - + message: '#^Property App\\Entity\\Neighbourhood\:\:\$boundingPolygon has no type specified\.$#' + identifier: missingType.property + count: 1 + path: src/Entity/Neighbourhood.php + + - + message: '#^Property App\\Entity\\Neighbourhood\:\:\$id \(int\|null\) is never assigned int so it can be removed from the property type\.$#' + identifier: property.unusedType + count: 1 + path: src/Entity/Neighbourhood.php + + - + message: '#^Property App\\Entity\\Problem\:\:\$id \(int\|null\) is never assigned int so it can be removed from the property type\.$#' + identifier: property.unusedType + count: 1 + path: src/Entity/Problem.php + + - + message: '#^Property App\\Entity\\Settings\:\:\$id \(int\|null\) is never assigned int so it can be removed from the property type\.$#' + identifier: property.unusedType + count: 1 + path: src/Entity/Settings.php + + - + message: '#^Method App\\Entity\\User\:\:setRoles\(\) has parameter \$roles with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Entity/User.php + + - + message: '#^Property App\\Entity\\User\:\:\$id \(int\|null\) is never assigned int so it can be removed from the property type\.$#' + identifier: property.unusedType + count: 1 + path: src/Entity/User.php + + - + message: '#^Property App\\Entity\\User\:\:\$roles has no type specified\.$#' + identifier: missingType.property + count: 1 + path: src/Entity/User.php + + - + message: '#^Call to an undefined method Doctrine\\Common\\Collections\\Collection\\:\:matching\(\)\.$#' + identifier: method.notFound + count: 4 + path: src/Entity/Wander.php + + - + message: '#^Invalid array key type float\.$#' + identifier: offsetAccess.invalidOffset + count: 1 + path: src/Entity/Wander.php + + - + message: '#^Method App\\Entity\\Wander\:\:getImagesWithNoAutoTags\(\) return type with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' + identifier: missingType.generics + count: 1 + path: src/Entity/Wander.php + + - + message: '#^Method App\\Entity\\Wander\:\:getImagesWithNoLatLng\(\) return type with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' + identifier: missingType.generics + count: 1 + path: src/Entity/Wander.php + + - + message: '#^Method App\\Entity\\Wander\:\:getImagesWithNoNeighbourhood\(\) return type with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' + identifier: missingType.generics + count: 1 + path: src/Entity/Wander.php + + - + message: '#^Method App\\Entity\\Wander\:\:getImagesWithNoRating\(\) return type with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' + identifier: missingType.generics + count: 1 + path: src/Entity/Wander.php + + - + message: '#^Method App\\Entity\\Wander\:\:getImagesWithNoTags\(\) return type with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' + identifier: missingType.generics + count: 1 + path: src/Entity/Wander.php + + - + message: '#^Method App\\Entity\\Wander\:\:getImagesWithNoTitle\(\) return type with generic interface Doctrine\\Common\\Collections\\Collection does not specify its types\: TKey, T$#' + identifier: missingType.generics + count: 1 + path: src/Entity/Wander.php + + - + message: '#^Method App\\Entity\\Wander\:\:isTimeLengthSuspicious\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: src/Entity/Wander.php + + - + message: '#^Property App\\Entity\\Wander\:\:\$centroid has no type specified\.$#' + identifier: missingType.property + count: 1 + path: src/Entity/Wander.php + + - + message: '#^Property App\\Entity\\Wander\:\:\$id \(int\|null\) is never assigned int so it can be removed from the property type\.$#' + identifier: property.unusedType + count: 1 + path: src/Entity/Wander.php + + - + message: '#^Property App\\Entity\\Wander\:\:\$images has no type specified\.$#' + identifier: missingType.property + count: 1 + path: src/Entity/Wander.php + + - + message: '#^Method App\\EventListener\\ImageUploadListener\:\:onVichUploaderPostUpload\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: src/EventListener/ImageUploadListener.php + + - + message: '#^Method App\\EventListener\\WanderDeleteListener\:\:postRemove\(\) has parameter \$event with generic class Doctrine\\Persistence\\Event\\LifecycleEventArgs but does not specify its types\: TObjectManager$#' + identifier: missingType.generics + count: 1 + path: src/EventListener/WanderDeleteListener.php + + - + message: '#^Method App\\EventListener\\WanderDeleteListener\:\:preRemove\(\) has parameter \$event with generic class Doctrine\\Persistence\\Event\\LifecycleEventArgs but does not specify its types\: TObjectManager$#' + identifier: missingType.generics + count: 1 + path: src/EventListener/WanderDeleteListener.php + + - + message: '#^Method App\\EventListener\\WanderUploadListener\:\:prePersist\(\) has parameter \$event with generic class Doctrine\\Persistence\\Event\\LifecycleEventArgs but does not specify its types\: TObjectManager$#' + identifier: missingType.generics + count: 1 + path: src/EventListener/WanderUploadListener.php + + - + message: '#^Parameter \#1 \$from of method App\\Repository\\ImageRepository\:\:findBetweenDates\(\) expects DateTime, DateTimeInterface given\.$#' + identifier: argument.type + count: 1 + path: src/EventListener/WanderUploadListener.php + + - + message: '#^Parameter \#2 \$to of method App\\Repository\\ImageRepository\:\:findBetweenDates\(\) expects DateTime, DateTimeInterface given\.$#' + identifier: argument.type + count: 1 + path: src/EventListener/WanderUploadListener.php + + - + message: '#^Property App\\Form\\ImageFilterData\:\:\$ratingComparison \(string\) on left side of \?\? is not nullable\.$#' + identifier: nullCoalesce.property + count: 1 + path: src/Form/ImageFilterData.php + + - + message: '#^Method App\\Message\\WarmImageCache\:\:__construct\(\) has parameter \$filters with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Message/WarmImageCache.php + + - + message: '#^Method App\\Message\\WarmImageCache\:\:getFilters\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Message/WarmImageCache.php + + - + message: '#^Call to method setContainer\(\) on an unknown class Symfony\\Component\\DependencyInjection\\ContainerAwareInterface\.$#' + identifier: class.notFound + count: 1 + path: src/Migrations/Factory/MigrationFactoryDecorator.php + + - + message: '#^Class Symfony\\Component\\DependencyInjection\\ContainerAwareInterface not found\.$#' + identifier: class.notFound + count: 1 + path: src/Migrations/Factory/MigrationFactoryDecorator.php + + - + message: '#^Class App\\Repository\\ImageRepository has PHPDoc tag @method for method findBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/ImageRepository.php + + - + message: '#^Class App\\Repository\\ImageRepository has PHPDoc tag @method for method findBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/ImageRepository.php + + - + message: '#^Class App\\Repository\\ImageRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/ImageRepository.php + + - + message: '#^Class App\\Repository\\ImageRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/ImageRepository.php + + - + message: '#^Method App\\Repository\\ImageRepository\:\:findBetweenDates\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: src/Repository/ImageRepository.php + + - + message: '#^Method App\\Repository\\ImageRepository\:\:findFromIdOnwards\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: src/Repository/ImageRepository.php + + - + message: '#^Method App\\Repository\\ImageRepository\:\:findNext\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: src/Repository/ImageRepository.php + + - + message: '#^Method App\\Repository\\ImageRepository\:\:findPrev\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: src/Repository/ImageRepository.php + + - + message: '#^Method App\\Repository\\ImageRepository\:\:findWithHasLatLng\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: src/Repository/ImageRepository.php + + - + message: '#^Method App\\Repository\\ImageRepository\:\:findWithNoNeighbourhood\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: src/Repository/ImageRepository.php + + - + message: '#^Method App\\Repository\\ImageRepository\:\:findWithNoNeighbourhoodButHasLatLng\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: src/Repository/ImageRepository.php + + - + message: '#^Method App\\Repository\\ImageRepository\:\:findWithNoStreet\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: src/Repository/ImageRepository.php + + - + message: '#^Method App\\Repository\\ImageRepository\:\:findWithNoStreetButHasLatLng\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: src/Repository/ImageRepository.php + + - + message: '#^Method App\\Repository\\ImageRepository\:\:findWithNoWander\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: src/Repository/ImageRepository.php + + - + message: '#^Method App\\Repository\\ImageRepository\:\:getAllNeighbourhoods\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/ImageRepository.php + + - + message: '#^Class App\\Repository\\NeighbourhoodRepository has PHPDoc tag @method for method findBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/NeighbourhoodRepository.php + + - + message: '#^Class App\\Repository\\NeighbourhoodRepository has PHPDoc tag @method for method findBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/NeighbourhoodRepository.php + + - + message: '#^Class App\\Repository\\NeighbourhoodRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/NeighbourhoodRepository.php + + - + message: '#^Class App\\Repository\\NeighbourhoodRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/NeighbourhoodRepository.php + + - + message: '#^Class App\\Repository\\ProblemRepository has PHPDoc tag @method for method findBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/ProblemRepository.php + + - + message: '#^Class App\\Repository\\ProblemRepository has PHPDoc tag @method for method findBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/ProblemRepository.php + + - + message: '#^Class App\\Repository\\ProblemRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/ProblemRepository.php + + - + message: '#^Class App\\Repository\\ProblemRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/ProblemRepository.php + + - + message: '#^Class App\\Repository\\SettingsRepository has PHPDoc tag @method for method findBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/SettingsRepository.php + + - + message: '#^Class App\\Repository\\SettingsRepository has PHPDoc tag @method for method findBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/SettingsRepository.php + + - + message: '#^Class App\\Repository\\SettingsRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/SettingsRepository.php + + - + message: '#^Class App\\Repository\\SettingsRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/SettingsRepository.php + + - + message: '#^Class App\\Repository\\TagRepository has PHPDoc tag @method for method findBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/TagRepository.php + + - + message: '#^Class App\\Repository\\TagRepository has PHPDoc tag @method for method findBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/TagRepository.php + + - + message: '#^Class App\\Repository\\TagRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/TagRepository.php + + - + message: '#^Class App\\Repository\\TagRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/TagRepository.php + + - + message: '#^Class App\\Repository\\WanderRepository has PHPDoc tag @method for method findBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/WanderRepository.php + + - + message: '#^Class App\\Repository\\WanderRepository has PHPDoc tag @method for method findBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/WanderRepository.php + + - + message: '#^Class App\\Repository\\WanderRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#1 \$criteria with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/WanderRepository.php + + - + message: '#^Class App\\Repository\\WanderRepository has PHPDoc tag @method for method findOneBy\(\) parameter \#2 \$orderBy with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Repository/WanderRepository.php + + - + message: '#^Method App\\Repository\\WanderRepository\:\:addWhereHasImages\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: src/Repository/WanderRepository.php + + - + message: '#^Method App\\Repository\\WanderRepository\:\:findAverageDistance\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: src/Repository/WanderRepository.php + + - + message: '#^Method App\\Repository\\WanderRepository\:\:findFirstWhereIncludesDate\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: src/Repository/WanderRepository.php + + - + message: '#^Method App\\Repository\\WanderRepository\:\:findLongest\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: src/Repository/WanderRepository.php + + - + message: '#^Method App\\Repository\\WanderRepository\:\:findShortest\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: src/Repository/WanderRepository.php + + - + message: '#^Method App\\Repository\\WanderRepository\:\:findWhereIncludesDate\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: src/Repository/WanderRepository.php + + - + message: '#^Unreachable statement \- code above always terminates\.$#' + identifier: deadCode.unreachable + count: 1 + path: src/Service/DummyLocationTaggingService.php + + - + message: '#^Iterating over an object of an unknown class Google\\Protobuf\\Internal\\RepeatedField\.$#' + identifier: class.notFound + count: 2 + path: src/Service/GoogleImageTaggingService.php + + - + message: '#^Property App\\Service\\GoogleImageTaggingService\:\:\$imagine has no type specified\.$#' + identifier: missingType.property + count: 1 + path: src/Service/GoogleImageTaggingService.php + + - + message: '#^Unreachable statement \- code above always terminates\.$#' + identifier: deadCode.unreachable + count: 1 + path: src/Service/GoogleLocationTaggingService.php + + - + message: '#^PHPDoc tag @var with type App\\Utils\\ExifHelperInterface is not subtype of native type App\\Utils\\ExifHelper\.$#' + identifier: varTag.nativeType + count: 2 + path: src/Service/ImageService.php + + - + message: '#^Method App\\Service\\MarkdownService\:\:findLinks\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Service/MarkdownService.php + + - + message: '#^Deprecated in PHP 8\.4\: Parameter \#2 \$format \(string\) is implicitly nullable via default value null\.$#' + identifier: parameter.implicitlyNullable + count: 2 + path: src/Twig/GeneralRuntime.php + + - + message: '#^Method App\\Twig\\SrcsetRuntime\:\:srcset\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: src/Twig/SrcsetRuntime.php + + - + message: '#^Property App\\Twig\\SrcsetRuntime\:\:\$filters has no type specified\.$#' + identifier: missingType.property + count: 1 + path: src/Twig/SrcsetRuntime.php + + - + message: '#^Call to function is_string\(\) with array\|bool will always evaluate to false\.$#' + identifier: function.impossibleType + count: 2 + path: src/Utils/ExifHelper.php + + - + message: '#^Method App\\Utils\\ExifHelper\:\:getGPS\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Utils/ExifHelper.php + + - + message: '#^Method App\\Utils\\ExifHelper\:\:getKeywords\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Utils/ExifHelper.php + + - + message: '#^Method App\\Utils\\ExifHelperInterface\:\:getGPS\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Utils/ExifHelperInterface.php + + - + message: '#^Method App\\Utils\\ExifHelperInterface\:\:getKeywords\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Utils/ExifHelperInterface.php + + - + message: '#^PHPDoc tag @var does not specify variable name\.$#' + identifier: varTag.noVariable + count: 1 + path: tests/Controller/WanderControllerTest.php + + - + message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertNotNull\(\) with App\\Entity\\Image and ''Featured Image…'' will always evaluate to true\.$#' + identifier: method.alreadyNarrowedType + count: 1 + path: tests/FeaturedImageTest.php + + - + message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertNotNull\(\) with App\\Entity\\Wander and ''Featuring Wander…'' will always evaluate to true\.$#' + identifier: method.alreadyNarrowedType + count: 1 + path: tests/FeaturedImageTest.php + + - + message: '#^Method App\\Tests\\FeaturedImageTest\:\:testChangeFeaturedImageFromWanderSide\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/FeaturedImageTest.php + + - + message: '#^Method App\\Tests\\FeaturedImageTest\:\:testDeleteFeaturedImage\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/FeaturedImageTest.php + + - + message: '#^Method App\\Tests\\FeaturedImageTest\:\:testDeleteFeaturingWander\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/FeaturedImageTest.php + + - + message: '#^Method App\\Tests\\FeaturedImageTest\:\:testHasFeaturedImage\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/FeaturedImageTest.php + + - + message: '#^Method App\\Tests\\FeaturedImageTest\:\:testSetAsFeaturedImage\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/FeaturedImageTest.php + + - + message: '#^Method App\\Tests\\FeaturedImageTest\:\:testSetAsFeaturedImageWhenNoWanderAssociated\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/FeaturedImageTest.php + + - + message: '#^Method App\\Tests\\FeaturedImageTest\:\:testSetFeaturedImageFromImageSide\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/FeaturedImageTest.php + + - + message: '#^Method App\\Tests\\FeaturedImageTest\:\:testSetFeaturedImageFromWanderSide\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/FeaturedImageTest.php + + - + message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertIsIterable\(\) with Doctrine\\Common\\Collections\\Collection\ will always evaluate to true\.$#' + identifier: method.alreadyNarrowedType + count: 1 + path: tests/ImageServiceTest.php + + - + message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertIsIterable\(\) with array\ and ''Reading an image…'' will always evaluate to true\.$#' + identifier: method.alreadyNarrowedType + count: 2 + path: tests/ImageServiceTest.php + + - + message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertIsIterable\(\) with array\ and ''Reading image with…'' will always evaluate to true\.$#' + identifier: method.alreadyNarrowedType + count: 1 + path: tests/ImageServiceTest.php + + - + message: '#^Method App\\Tests\\ImageServiceTest\:\:testCapturedAt\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/ImageServiceTest.php + + - + message: '#^Method App\\Tests\\ImageServiceTest\:\:testMinimalMetadata\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/ImageServiceTest.php + + - + message: '#^Method App\\Tests\\ImageServiceTest\:\:testNonJPEG\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/ImageServiceTest.php + + - + message: '#^Method App\\Tests\\ImageServiceTest\:\:testNumericDescription\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/ImageServiceTest.php + + - + message: '#^Method App\\Tests\\ImageServiceTest\:\:testNumericTitle\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/ImageServiceTest.php + + - + message: '#^Method App\\Tests\\ImageServiceTest\:\:testReadCoords\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/ImageServiceTest.php + + - + message: '#^Method App\\Tests\\ImageServiceTest\:\:testReadCopyright\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/ImageServiceTest.php + + - + message: '#^Method App\\Tests\\ImageServiceTest\:\:testReadDescription\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/ImageServiceTest.php + + - + message: '#^Method App\\Tests\\ImageServiceTest\:\:testReadLocationText\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/ImageServiceTest.php + + - + message: '#^Method App\\Tests\\ImageServiceTest\:\:testReadRating\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/ImageServiceTest.php + + - + message: '#^Method App\\Tests\\ImageServiceTest\:\:testReadTitle\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/ImageServiceTest.php + + - + message: '#^Method App\\Tests\\ImageServiceTest\:\:testSetNeighbourhoodFromLatLng\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/ImageServiceTest.php + + - + message: '#^Method App\\Tests\\ImageServiceTest\:\:testStandaloneNeighbourhoodSetter\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/ImageServiceTest.php + + - + message: '#^Method App\\Tests\\ImageServiceTest\:\:testTags\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/ImageServiceTest.php + + - + message: '#^Method App\\Tests\\ImageServiceTest\:\:testToolPath\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/ImageServiceTest.php + + - + message: '#^Property App\\Tests\\ImageServiceTest\:\:\$neighbourhoodService \(App\\Service\\NeighbourhoodService&PHPUnit\\Framework\\MockObject\\MockObject\) does not accept App\\Service\\NeighbourhoodService&PHPUnit\\Framework\\MockObject\\Stub\.$#' + identifier: assign.propertyType + count: 1 + path: tests/ImageServiceTest.php + + - + message: '#^Return type of call to method PHPUnit\\Framework\\TestCase\:\:createStub\(\) contains unresolvable type\.$#' + identifier: method.unresolvableReturnType + count: 2 + path: tests/ImageServiceTest.php + + - + message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertIsArray\(\) with array\ and ''Expected an array…'' will always evaluate to true\.$#' + identifier: method.alreadyNarrowedType + count: 2 + path: tests/ImageTagTest.php + + - + message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertIsArray\(\) with array\ and ''Expected array back…'' will always evaluate to true\.$#' + identifier: method.alreadyNarrowedType + count: 1 + path: tests/ImageTagTest.php + + - + message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertIsIterable\(\) with Doctrine\\Common\\Collections\\Collection\ and ''GetTags\(\) should…'' will always evaluate to true\.$#' + identifier: method.alreadyNarrowedType + count: 1 + path: tests/ImageTagTest.php + + - + message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertIsIterable\(\) with iterable\ and ''GetTags\(\) should…'' will always evaluate to true\.$#' + identifier: method.alreadyNarrowedType + count: 1 + path: tests/ImageTagTest.php + + - + message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertNotNull\(\) with Beelab\\TagBundle\\Tag\\TagInterface and ''Retrieved tag…'' will always evaluate to true\.$#' + identifier: method.alreadyNarrowedType + count: 1 + path: tests/ImageTagTest.php + + - + message: '#^PHPDoc tag @var with type array\ is not subtype of native type array\{''foo'', ''bar'', ''baz''\}\.$#' + identifier: varTag.nativeType + count: 2 + path: tests/ImageTagTest.php + + - + message: '#^Method App\\Tests\\LocationServiceTest\:\:testGetLocationName\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/LocationServiceTest.php + + - + message: '#^Method App\\Tests\\MarkdownServiceTest\:\:testMarkdownToText\(\) has parameter \$expected with no type specified\.$#' + identifier: missingType.parameter + count: 1 + path: tests/MarkdownServiceTest.php + + - + message: '#^Method App\\Tests\\MarkdownServiceTest\:\:testMarkdownToText\(\) has parameter \$in with no type specified\.$#' + identifier: missingType.parameter + count: 1 + path: tests/MarkdownServiceTest.php + + - + message: '#^Call to function method_exists\(\) with ''Symfony\\\\Component\\\\Dotenv\\\\Dotenv'' and ''bootEnv'' will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType + count: 1 + path: tests/bootstrap.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist index cef79b41..890ba173 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,3 +1,6 @@ +includes: + - phpstan-baseline.neon + parameters: level: 6 paths: From e8e028a20c7f016a13df38cbd200345ba2b44c0b Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Sun, 2 Aug 2026 21:57:26 +0100 Subject: [PATCH 06/14] Pin PHPStan phpVersion to 8.3 Baseline was generated under a newer local PHP (post-Homebrew-upgrade), which changed wording for the implicitly-nullable-parameter deprecation check and broke CI's baseline match against the pinned 8.3 target. --- phpstan-baseline.neon | 13 +------------ phpstan.neon.dist | 1 + 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 11e04146..c15563cf 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,11 +1,5 @@ parameters: ignoreErrors: - - - message: '#^Deprecated in PHP 8\.4\: Parameter \#4 \$format \(string\) is implicitly nullable via default value null\.$#' - identifier: parameter.implicitlyNullable - count: 2 - path: src/Controller/Api/ApiController.php - - message: '#^Method App\\DataFixtures\\NeighbourhoodFixtures\:\:getPolygonRings\(\) has no return type specified\.$#' identifier: missingType.return @@ -510,12 +504,6 @@ parameters: count: 1 path: src/Service/MarkdownService.php - - - message: '#^Deprecated in PHP 8\.4\: Parameter \#2 \$format \(string\) is implicitly nullable via default value null\.$#' - identifier: parameter.implicitlyNullable - count: 2 - path: src/Twig/GeneralRuntime.php - - message: '#^Method App\\Twig\\SrcsetRuntime\:\:srcset\(\) has no return type specified\.$#' identifier: missingType.return @@ -803,3 +791,4 @@ parameters: identifier: function.alreadyNarrowedType count: 1 path: tests/bootstrap.php + diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 890ba173..76bd18b8 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -3,6 +3,7 @@ includes: parameters: level: 6 + phpVersion: 80300 paths: - bin/ - config/ From 50c21734dc572437019ac29bf88caba288e7786f Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Sun, 2 Aug 2026 22:04:34 +0100 Subject: [PATCH 07/14] Pin GitHub Actions to commit hashes SonarCloud flagged floating version tags (e.g. @v4) as a supply-chain risk. Pin every action to its resolved commit SHA, with the version kept as a trailing comment for readability. Also bumps github/codeql-action from v2 to v4 (latest major) while in the file. --- .github/workflows/ci.yml | 18 +++++++++--------- .github/workflows/codeql-analysis.yml | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e46868f0..2d943ab7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,13 +70,13 @@ jobs: TEST_EXIFTOOL_PATH: /usr/bin/exiftool steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - name: Install exiftool run: sudo apt-get update && sudo apt-get install -y exiftool - name: Setup PHP - uses: shivammathur/setup-php@v2 + uses: shivammathur/setup-php@b604ade2a87db23f8871b7182e69ec5e75effb45 # v2 with: php-version: ${{ env.PHP_VERSION }} extensions: ctype, iconv, pdo, pdo_mysql, mysqli, zip, apcu, bcmath, intl, gd @@ -88,7 +88,7 @@ jobs: run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT" - name: Cache composer packages - uses: actions/cache@v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} @@ -114,10 +114,10 @@ jobs: name: PHPStan runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - name: Setup PHP - uses: shivammathur/setup-php@v2 + uses: shivammathur/setup-php@b604ade2a87db23f8871b7182e69ec5e75effb45 # v2 with: php-version: ${{ env.PHP_VERSION }} coverage: none @@ -128,7 +128,7 @@ jobs: run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT" - name: Cache composer packages - uses: actions/cache@v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} @@ -144,10 +144,10 @@ jobs: name: Rector (dry run) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - name: Setup PHP - uses: shivammathur/setup-php@v2 + uses: shivammathur/setup-php@b604ade2a87db23f8871b7182e69ec5e75effb45 # v2 with: php-version: ${{ env.PHP_VERSION }} coverage: none @@ -158,7 +158,7 @@ jobs: run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT" - name: Cache composer packages - uses: actions/cache@v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 1cd9e3ee..497cdb69 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -38,11 +38,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@a37ce9120846195fa4ece8f58b268e6043cb2f26 # v3.7.0 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -56,7 +56,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -69,6 +69,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4 with: category: "/language:${{matrix.language}}" From 141cdb55deba7ed5eb7a6e565151e9f41d510f1f Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Sun, 2 Aug 2026 22:11:48 +0100 Subject: [PATCH 08/14] Fix mariadb service health check in CI mariadb:11 now resolves to 11.8.8, which dropped the mysqladmin binary the health-cmd relied on. The command silently failed to execute, so the service was never marked healthy even though MariaDB itself started fine. Switch to healthcheck.sh, the image's own bundled health-check script. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d943ab7..00f62a4e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: ports: - 3306:3306 options: >- - --health-cmd="mysqladmin ping -h 127.0.0.1 -u root -psecret" + --health-cmd="healthcheck.sh --connect --innodb_initialized" --health-interval=10s --health-timeout=5s --health-retries=10 From 51e618e59fb80e3d61a2e89303f5ef92bd15ac39 Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Sun, 2 Aug 2026 22:23:04 +0100 Subject: [PATCH 09/14] Set SECURE_SCHEME=http for CI test run Controller tests read getenv('SECURE_SCHEME') to decide whether to simulate an HTTPS request, but that only sees real process env vars, not .env-loaded values (Symfony's Dotenv doesn't call putenv() by default). CI has no docker-compose layer to supply this like local dev/test does, so every request was treated as insecure and hit the requires_channel https redirect in security.yaml, failing 12 tests. --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 00f62a4e..47db765b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,6 +68,12 @@ jobs: # Ubuntu's apt package puts it here, not the /usr/local/bin the # phpunit.dist.xml default assumes (that's a Docker-image path). TEST_EXIFTOOL_PATH: /usr/bin/exiftool + # Controller tests read getenv('SECURE_SCHEME') to decide whether to + # simulate an HTTPS request (see security.yaml's access_control). That + # only sees real process env vars, not values from .env, so it must be + # set here explicitly — docker-compose.app.yml does the same for local + # dev/test. + SECURE_SCHEME: http steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 From bd0cf99ff23846631f8f47db83752e749dce88ba Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Sun, 2 Aug 2026 22:31:33 +0100 Subject: [PATCH 10/14] Build front-end assets before running tests in CI base.html.twig calls encore_entry_link_tags()/encore_entry_script_tags(), which need public/build/entrypoints.json (git-ignored Webpack Encore output). CI only ran composer install, so that file never existed and any test rendering the base template 500'd. Add actions/setup-node (Node 22, matching the Dockerfile) plus yarn install/build before the test suite runs. --- .github/workflows/ci.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 47db765b..503dce34 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,6 +103,21 @@ jobs: - name: Install dependencies run: composer install --prefer-dist --no-progress --no-interaction + - name: Setup Node + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: '22' + cache: yarn + + - name: Install JS dependencies + run: yarn install --frozen-lockfile + + # base.html.twig calls encore_entry_link_tags()/encore_entry_script_tags(), + # which need public/build/entrypoints.json (git-ignored, Webpack Encore + # output) to exist, or any test that renders it 500s. + - name: Build front-end assets + run: yarn build + # Actions blocks these steps until the health checks above report # healthy, so unlike docker/testentrypoint.sh's wait-for-it.sh dance, # there's nothing to poll for manually here. From 9e4433bedeedcdb81d288a5a78238ac9092fe501 Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Sun, 2 Aug 2026 23:35:08 +0100 Subject: [PATCH 11/14] Upgrade twig/twig to fix PHPUnit 12 IntegrationTestCase incompatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v3.24.0's IntegrationTestCase used a non-static getTests() as its @dataProvider, which PHPUnit 12 refuses to call, throwing ArgumentCountError for testIntegration()/testLegacyIntegration() in TwigGeneralExtensionTest and TwigMarkdownExtraFilterTest. Fixed upstream in twigphp/Twig#4826 (static provideTests()/ provideLegacyTests() with #[DataProvider] attributes), merged into the 3.x branch and released in v3.28.0. No composer.json change needed — the existing ^3.0 constraint already allows it. --- composer.lock | 54 +++++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/composer.lock b/composer.lock index e3fad260..f9badb6c 100644 --- a/composer.lock +++ b/composer.lock @@ -7217,16 +7217,16 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.6.0", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d", + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d", "shasum": "" }, "require": { @@ -7239,7 +7239,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -7264,7 +7264,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.1" }, "funding": [ { @@ -7275,12 +7275,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2026-06-05T06:23:12+00:00" }, { "name": "symfony/doctrine-bridge", @@ -9879,16 +9883,16 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.33.0", + "version": "v1.38.2", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", "shasum": "" }, "require": { @@ -9940,7 +9944,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.2" }, "funding": [ { @@ -9960,20 +9964,20 @@ "type": "tidelift" } ], - "time": "2024-12-23T08:48:59+00:00" + "time": "2026-05-27T06:59:30+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", "shasum": "" }, "require": { @@ -10024,7 +10028,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0" }, "funding": [ { @@ -10044,7 +10048,7 @@ "type": "tidelift" } ], - "time": "2025-01-02T08:10:11+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/polyfill-php82", @@ -13068,16 +13072,16 @@ }, { "name": "twig/twig", - "version": "v3.24.0", + "version": "v3.28.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "a6769aefb305efef849dc25c9fd1653358c148f0" + "reference": "597c12ed286fb9d1701a36684ce6e0cbe28ebc8b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/a6769aefb305efef849dc25c9fd1653358c148f0", - "reference": "a6769aefb305efef849dc25c9fd1653358c148f0", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/597c12ed286fb9d1701a36684ce6e0cbe28ebc8b", + "reference": "597c12ed286fb9d1701a36684ce6e0cbe28ebc8b", "shasum": "" }, "require": { @@ -13132,7 +13136,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.24.0" + "source": "https://github.com/twigphp/Twig/tree/v3.28.0" }, "funding": [ { @@ -13144,7 +13148,7 @@ "type": "tidelift" } ], - "time": "2026-03-17T21:31:11+00:00" + "time": "2026-07-03T20:44:34+00:00" }, { "name": "vich/uploader-bundle", From e0d098fed36a6badad77f6dee3ebb7a48d4c3a0a Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Sun, 2 Aug 2026 23:48:40 +0100 Subject: [PATCH 12/14] Apply outstanding Rector refactors Mostly mechanical: declare(strict_types=1) added where missing (SafeDeclareStrictTypesRector) and #[\Override] added to overridden migration methods (AddOverrideAttributeToOverriddenMethodsRector), plus assorted coding-style and code-quality fixes. Verified clean with PHPStan and no new PHPUnit failures (remaining failures are pre-existing local-only Elasticsearch connectivity gaps). --- config/bundles.php | 2 ++ config/preload.php | 2 ++ public/index.php | 4 +--- src/CacheKernel.php | 2 ++ src/Controller/AboutController.php | 2 ++ src/Controller/Admin/AdminController.php | 2 ++ src/Controller/Admin/ImageController.php | 10 ++++++---- src/Controller/Admin/ProblemController.php | 3 +++ src/Controller/Admin/SettingsController.php | 2 ++ src/Controller/ExperimentalController.php | 3 ++- src/Controller/HomeController.php | 2 ++ src/Controller/Image/ImageController.php | 1 - src/Controller/SecurityController.php | 2 ++ src/Controller/UserController.php | 7 +++---- src/DataFixtures/AppFixtures.php | 2 ++ .../ImageCalculatedFieldSetterListener.php | 4 +++- src/EventListener/ImageGeneralPostPersistListener.php | 2 ++ src/EventListener/ImageUploadListener.php | 2 ++ src/EventListener/SearchIndexer.php | 2 ++ src/EventListener/StatsCacheClearer.php | 2 ++ src/EventListener/WanderDeleteListener.php | 4 +++- src/Exception/ThirdPartyAPIException.php | 2 ++ src/Form/ImageFilterType.php | 2 ++ src/Form/SettingsType.php | 2 ++ src/Form/UserChangePasswordType.php | 2 ++ src/Form/WanderType.php | 2 ++ src/Kernel.php | 2 ++ src/Message/GeolocateImage.php | 2 ++ src/Message/RecogniseImage.php | 4 +++- src/Message/WarmImageCache.php | 4 +++- src/Migrations/Version20201109203210.php | 2 ++ src/Migrations/Version20201110220220.php | 2 ++ src/Migrations/Version20201110234239.php | 2 ++ src/Migrations/Version20201113212713.php | 2 ++ src/Migrations/Version20201113213010.php | 2 ++ src/Migrations/Version20201114161247.php | 2 ++ src/Migrations/Version20201114163020.php | 2 ++ src/Migrations/Version20201116203128.php | 2 ++ src/Migrations/Version20201116204001.php | 2 ++ src/Migrations/Version20201116210210.php | 4 +++- src/Migrations/Version20201116210416.php | 6 ++++-- src/Migrations/Version20201117194218.php | 2 ++ src/Migrations/Version20201117194439.php | 2 ++ src/Migrations/Version20201117213616.php | 4 +++- src/Migrations/Version20201117214638.php | 4 +++- src/Migrations/Version20201121235950.php | 2 ++ src/Migrations/Version20201122104502.php | 2 ++ src/Migrations/Version20201126220805.php | 2 ++ src/Migrations/Version20201201201808.php | 2 ++ src/Migrations/Version20201205103341.php | 2 ++ src/Migrations/Version20201213144221.php | 2 ++ src/Migrations/Version20210125191023.php | 4 +++- src/Migrations/Version20210401194805.php | 4 +++- src/Migrations/Version20210405202940.php | 2 ++ src/Migrations/Version20210408221450.php | 4 +++- src/Migrations/Version20210412170344.php | 2 ++ src/Migrations/Version20210413195948.php | 2 ++ src/Migrations/Version20210415185234.php | 5 ++++- src/Migrations/Version20210423172136.php | 2 ++ src/Migrations/Version20210424125341.php | 2 ++ src/Migrations/Version20210424131056.php | 4 ++++ src/Migrations/Version20210427200452.php | 4 +++- src/Migrations/Version20210509222909.php | 3 +++ src/Migrations/Version20210519211549.php | 2 ++ src/Migrations/Version20210520201508.php | 2 ++ src/Migrations/Version20210531094747.php | 2 ++ src/Migrations/Version20210620130007.php | 4 +++- src/Migrations/Version20211120120437.php | 2 ++ src/Migrations/Version20211208211945.php | 2 ++ src/Migrations/Version20211213220450.php | 5 +++++ src/Migrations/Version20211221090120.php | 2 ++ src/Migrations/Version20211221094902.php | 2 ++ src/Migrations/Version20260201203811.php | 2 ++ src/Migrations/Version20260201215733.php | 2 ++ src/Migrations/Version20260301192810.php | 4 +++- src/Migrations/Version20260301200336.php | 2 ++ src/Repository/ProblemRepository.php | 2 ++ src/Repository/TagRepository.php | 2 ++ src/Repository/UserRepository.php | 2 ++ src/Service/DiskStatsService.php | 2 ++ src/Service/DummyImageTaggingService.php | 2 ++ src/Service/DummyLocationTaggingService.php | 2 ++ src/Service/ImageTaggingServiceInterface.php | 4 +++- src/Service/ImaggaService.php | 2 ++ src/Service/LocationTaggingServiceInterface.php | 2 ++ src/Service/SpellingService.php | 2 ++ src/Service/UploadHelper.php | 2 +- src/Twig/GeneralExtension.php | 2 ++ src/Twig/SrcsetExtension.php | 4 +++- src/Twig/WanderExtension.php | 4 +++- src/Twig/WanderRuntime.php | 2 ++ src/Utils/ExifHelper.php | 2 +- src/Utils/ExifHelperInterface.php | 2 ++ tests/ImageTagTest.php | 6 +++--- tests/symfony-container.php | 2 ++ 95 files changed, 218 insertions(+), 37 deletions(-) diff --git a/config/bundles.php b/config/bundles.php index d5e6975e..fa9f1829 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -1,5 +1,7 @@ ['all' => true], Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], diff --git a/config/preload.php b/config/preload.php index 5ebcdb21..7cbe578d 100644 --- a/config/preload.php +++ b/config/preload.php @@ -1,5 +1,7 @@ new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); diff --git a/src/CacheKernel.php b/src/CacheKernel.php index a71937d8..2d9c404c 100644 --- a/src/CacheKernel.php +++ b/src/CacheKernel.php @@ -1,5 +1,7 @@ isCsrfTokenValid('set_neighbourhood'.$image->getId(), (string) $request->request->get('_token'))) { $neighbourhood = $this->neighbourhoodService->getNeighbourhood($image->getLatitude(), $image->getLongitude()); if ($neighbourhood !== null) { $image->setNeighbourhood($neighbourhood); - $entityManager->persist($image); - $entityManager->flush(); + $this->entityManager->persist($image); + $this->entityManager->flush(); } } diff --git a/src/Controller/Admin/ProblemController.php b/src/Controller/Admin/ProblemController.php index e087a70b..5fd23933 100644 --- a/src/Controller/Admin/ProblemController.php +++ b/src/Controller/Admin/ProblemController.php @@ -1,5 +1,7 @@ redirectToRoute('admin_problems_index'); } + $this->problemService->createProblemReport(); return $this->redirectToRoute('admin_problems_index'); } diff --git a/src/Controller/Admin/SettingsController.php b/src/Controller/Admin/SettingsController.php index 3ab9361c..7c613896 100644 --- a/src/Controller/Admin/SettingsController.php +++ b/src/Controller/Admin/SettingsController.php @@ -1,5 +1,7 @@ imageRepository->getEarliestImageCaptureDate(), $this->imageRepository->getLatestImageCaptureDate(), diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php index 2ec89313..0749855c 100644 --- a/src/Controller/SecurityController.php +++ b/src/Controller/SecurityController.php @@ -1,5 +1,7 @@ denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY'); /** @var \App\Entity\User | null */ @@ -51,7 +50,7 @@ public function changePassword( // TODO: Password strength validation? $password = $this->encoder->hashPassword($user, $plainPassword); $user->setPassword($password); - $entityManager->flush(); + $this->entityManager->flush(); $this->addFlash( 'notice', diff --git a/src/DataFixtures/AppFixtures.php b/src/DataFixtures/AppFixtures.php index 987f6fe9..3ae63e53 100644 --- a/src/DataFixtures/AppFixtures.php +++ b/src/DataFixtures/AppFixtures.php @@ -1,5 +1,7 @@ imageService->setCalculatedImageUris($image); } -} \ No newline at end of file +} diff --git a/src/EventListener/ImageGeneralPostPersistListener.php b/src/EventListener/ImageGeneralPostPersistListener.php index 9152a215..cad8fac6 100644 --- a/src/EventListener/ImageGeneralPostPersistListener.php +++ b/src/EventListener/ImageGeneralPostPersistListener.php @@ -1,5 +1,7 @@ logger->debug("Removing GPX file " . $path . " on deletion of wander " . $wander->getId()); unlink($path); } -} \ No newline at end of file +} diff --git a/src/Exception/ThirdPartyAPIException.php b/src/Exception/ThirdPartyAPIException.php index d54cc8f3..22e03322 100644 --- a/src/Exception/ThirdPartyAPIException.php +++ b/src/Exception/ThirdPartyAPIException.php @@ -1,5 +1,7 @@ overwrite; } -} \ No newline at end of file +} diff --git a/src/Message/WarmImageCache.php b/src/Message/WarmImageCache.php index 81033743..7f69d5d0 100644 --- a/src/Message/WarmImageCache.php +++ b/src/Message/WarmImageCache.php @@ -1,5 +1,7 @@ filters; } -} \ No newline at end of file +} diff --git a/src/Migrations/Version20201109203210.php b/src/Migrations/Version20201109203210.php index 977128e0..1df11352 100644 --- a/src/Migrations/Version20201109203210.php +++ b/src/Migrations/Version20201109203210.php @@ -12,6 +12,7 @@ */ final class Version20201109203210 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema) : void $this->addSql('CREATE TABLE wander (id INT AUTO_INCREMENT NOT NULL, title VARCHAR(255) NOT NULL, start_time DATETIME NOT NULL, end_time DATETIME NOT NULL, description VARCHAR(1024) DEFAULT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20201110220220.php b/src/Migrations/Version20201110220220.php index 926dea15..7f49e02f 100644 --- a/src/Migrations/Version20201110220220.php +++ b/src/Migrations/Version20201110220220.php @@ -12,6 +12,7 @@ */ final class Version20201110220220 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema) : void $this->addSql('ALTER TABLE wander ADD gpx_filename VARCHAR(255) NOT NULL'); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20201110234239.php b/src/Migrations/Version20201110234239.php index 5ec3b098..9df58f3a 100644 --- a/src/Migrations/Version20201110234239.php +++ b/src/Migrations/Version20201110234239.php @@ -12,6 +12,7 @@ */ final class Version20201110234239 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema) : void $this->addSql('CREATE TABLE user (id INT AUTO_INCREMENT NOT NULL, username VARCHAR(180) NOT NULL, roles JSON NOT NULL, password VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_8D93D649F85E0677 (username), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20201113212713.php b/src/Migrations/Version20201113212713.php index a097ebbc..6fbb0466 100644 --- a/src/Migrations/Version20201113212713.php +++ b/src/Migrations/Version20201113212713.php @@ -12,6 +12,7 @@ */ final class Version20201113212713 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema) : void $this->addSql('ALTER TABLE wander CHANGE description description LONGTEXT DEFAULT NULL'); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20201113213010.php b/src/Migrations/Version20201113213010.php index ec9b5ca3..5859080d 100644 --- a/src/Migrations/Version20201113213010.php +++ b/src/Migrations/Version20201113213010.php @@ -12,6 +12,7 @@ */ final class Version20201113213010 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema) : void $this->addSql('ALTER TABLE wander CHANGE title title VARCHAR(1024) NOT NULL'); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20201114161247.php b/src/Migrations/Version20201114161247.php index f060a432..26a679bb 100644 --- a/src/Migrations/Version20201114161247.php +++ b/src/Migrations/Version20201114161247.php @@ -12,6 +12,7 @@ */ final class Version20201114161247 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema) : void $this->addSql('CREATE TABLE settings (id INT AUTO_INCREMENT NOT NULL, site_title VARCHAR(255) DEFAULT NULL, site_subtitle VARCHAR(255) DEFAULT NULL, site_about LONGTEXT DEFAULT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20201114163020.php b/src/Migrations/Version20201114163020.php index 64482c07..b71fa3d0 100644 --- a/src/Migrations/Version20201114163020.php +++ b/src/Migrations/Version20201114163020.php @@ -12,6 +12,7 @@ */ final class Version20201114163020 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema) : void $this->addSql("INSERT INTO settings (site_title, site_subtitle, site_about) VALUES ('One-Mile Matt', 'Wanders of a Bristol Geek', 'TODO: Edit this About text')"); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20201116203128.php b/src/Migrations/Version20201116203128.php index b3ba8d68..69fe166c 100644 --- a/src/Migrations/Version20201116203128.php +++ b/src/Migrations/Version20201116203128.php @@ -12,6 +12,7 @@ */ final class Version20201116203128 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema) : void $this->addSql('CREATE TABLE image (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, title VARCHAR(255) DEFAULT NULL, description LONGTEXT DEFAULT NULL, size_in_bytes INT NOT NULL, mime_type VARCHAR(255) NOT NULL, original_name VARCHAR(255) DEFAULT NULL, dimensions VARCHAR(255) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20201116204001.php b/src/Migrations/Version20201116204001.php index 9c74dc2a..542f4e58 100644 --- a/src/Migrations/Version20201116204001.php +++ b/src/Migrations/Version20201116204001.php @@ -12,6 +12,7 @@ */ final class Version20201116204001 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema) : void $this->addSql('ALTER TABLE image ADD updated_at DATETIME NOT NULL'); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20201116210210.php b/src/Migrations/Version20201116210210.php index a94f73ef..6cb1ad51 100644 --- a/src/Migrations/Version20201116210210.php +++ b/src/Migrations/Version20201116210210.php @@ -12,6 +12,7 @@ */ final class Version20201116210210 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -20,9 +21,10 @@ public function getDescription() : string public function up(Schema $schema) : void { // this up() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE image ADD image_dimensions LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:simple_array)\', DROP dimensions'); + $this->addSql("ALTER TABLE image ADD image_dimensions LONGTEXT DEFAULT NULL COMMENT '(DC2Type:simple_array)', DROP dimensions"); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20201116210416.php b/src/Migrations/Version20201116210416.php index 246f34d5..7507392c 100644 --- a/src/Migrations/Version20201116210416.php +++ b/src/Migrations/Version20201116210416.php @@ -12,6 +12,7 @@ */ final class Version20201116210416 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -20,12 +21,13 @@ public function getDescription() : string public function up(Schema $schema) : void { // this up() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE image CHANGE image_dimensions dimensions LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:simple_array)\''); + $this->addSql("ALTER TABLE image CHANGE image_dimensions dimensions LONGTEXT DEFAULT NULL COMMENT '(DC2Type:simple_array)'"); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE image CHANGE dimensions image_dimensions LONGTEXT CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_unicode_ci` COMMENT \'(DC2Type:simple_array)\''); + $this->addSql("ALTER TABLE image CHANGE dimensions image_dimensions LONGTEXT CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_unicode_ci` COMMENT '(DC2Type:simple_array)'"); } } diff --git a/src/Migrations/Version20201117194218.php b/src/Migrations/Version20201117194218.php index 76fa82ba..121e6735 100644 --- a/src/Migrations/Version20201117194218.php +++ b/src/Migrations/Version20201117194218.php @@ -12,6 +12,7 @@ */ final class Version20201117194218 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema) : void $this->addSql('ALTER TABLE image CHANGE name name VARCHAR(255) DEFAULT NULL'); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20201117194439.php b/src/Migrations/Version20201117194439.php index 144a420a..0df82aa5 100644 --- a/src/Migrations/Version20201117194439.php +++ b/src/Migrations/Version20201117194439.php @@ -12,6 +12,7 @@ */ final class Version20201117194439 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema) : void $this->addSql('ALTER TABLE image CHANGE size_in_bytes size_in_bytes INT DEFAULT NULL, CHANGE mime_type mime_type VARCHAR(255) DEFAULT NULL'); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20201117213616.php b/src/Migrations/Version20201117213616.php index de4a8db7..0c8aae74 100644 --- a/src/Migrations/Version20201117213616.php +++ b/src/Migrations/Version20201117213616.php @@ -12,6 +12,7 @@ */ final class Version20201117213616 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -20,9 +21,10 @@ public function getDescription() : string public function up(Schema $schema) : void { // this up() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE image ADD latlng LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:simple_array)\''); + $this->addSql("ALTER TABLE image ADD latlng LONGTEXT DEFAULT NULL COMMENT '(DC2Type:simple_array)'"); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20201117214638.php b/src/Migrations/Version20201117214638.php index 3d417cd0..a4b8f540 100644 --- a/src/Migrations/Version20201117214638.php +++ b/src/Migrations/Version20201117214638.php @@ -12,6 +12,7 @@ */ final class Version20201117214638 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -20,9 +21,10 @@ public function getDescription() : string public function up(Schema $schema) : void { // this up() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE image ADD keywords LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:array)\''); + $this->addSql("ALTER TABLE image ADD keywords LONGTEXT DEFAULT NULL COMMENT '(DC2Type:array)'"); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20201121235950.php b/src/Migrations/Version20201121235950.php index 009da96a..4fcad76e 100644 --- a/src/Migrations/Version20201121235950.php +++ b/src/Migrations/Version20201121235950.php @@ -12,6 +12,7 @@ */ final class Version20201121235950 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema) : void $this->addSql('ALTER TABLE image ADD captured_at DATETIME DEFAULT NULL'); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20201122104502.php b/src/Migrations/Version20201122104502.php index fdbb74fd..c50f832b 100644 --- a/src/Migrations/Version20201122104502.php +++ b/src/Migrations/Version20201122104502.php @@ -12,6 +12,7 @@ */ final class Version20201122104502 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -28,6 +29,7 @@ public function up(Schema $schema) : void ) COLLATE utf8mb4_bin, ENGINE = InnoDB;'); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20201126220805.php b/src/Migrations/Version20201126220805.php index 1a20a725..89806ffd 100644 --- a/src/Migrations/Version20201126220805.php +++ b/src/Migrations/Version20201126220805.php @@ -12,6 +12,7 @@ */ final class Version20201126220805 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -25,6 +26,7 @@ public function up(Schema $schema) : void $this->addSql('ALTER TABLE wander_image ADD CONSTRAINT FK_69C7253A3DA5256D FOREIGN KEY (image_id) REFERENCES image (id) ON DELETE CASCADE'); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20201201201808.php b/src/Migrations/Version20201201201808.php index 9395a74b..320130ca 100644 --- a/src/Migrations/Version20201201201808.php +++ b/src/Migrations/Version20201201201808.php @@ -12,6 +12,7 @@ */ final class Version20201201201808 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema) : void $this->addSql('ALTER TABLE wander ADD distance DOUBLE PRECISION DEFAULT NULL, ADD avg_speed DOUBLE PRECISION DEFAULT NULL, ADD avg_pace DOUBLE PRECISION DEFAULT NULL, ADD min_altitude DOUBLE PRECISION DEFAULT NULL, ADD max_altitude DOUBLE PRECISION DEFAULT NULL, ADD cumulative_elevation_gain DOUBLE PRECISION DEFAULT NULL, ADD duration DOUBLE PRECISION DEFAULT NULL'); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20201205103341.php b/src/Migrations/Version20201205103341.php index 08fac430..7376db58 100644 --- a/src/Migrations/Version20201205103341.php +++ b/src/Migrations/Version20201205103341.php @@ -12,6 +12,7 @@ */ final class Version20201205103341 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema) : void $this->addSql('ALTER TABLE wander DROP duration'); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20201213144221.php b/src/Migrations/Version20201213144221.php index f5613870..609106b9 100644 --- a/src/Migrations/Version20201213144221.php +++ b/src/Migrations/Version20201213144221.php @@ -12,6 +12,7 @@ */ final class Version20201213144221 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema) : void $this->addSql('ALTER TABLE image ADD rating INT DEFAULT NULL'); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20210125191023.php b/src/Migrations/Version20210125191023.php index 10bc59f9..cd667d6f 100644 --- a/src/Migrations/Version20210125191023.php +++ b/src/Migrations/Version20210125191023.php @@ -12,6 +12,7 @@ */ final class Version20210125191023 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -20,9 +21,10 @@ public function getDescription() : string public function up(Schema $schema) : void { // this up() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE wander ADD centroid LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:array)\', ADD angle_from_home DOUBLE PRECISION DEFAULT NULL'); + $this->addSql("ALTER TABLE wander ADD centroid LONGTEXT DEFAULT NULL COMMENT '(DC2Type:array)', ADD angle_from_home DOUBLE PRECISION DEFAULT NULL"); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20210401194805.php b/src/Migrations/Version20210401194805.php index 5f328168..a9d8b1ba 100644 --- a/src/Migrations/Version20210401194805.php +++ b/src/Migrations/Version20210401194805.php @@ -12,6 +12,7 @@ */ final class Version20210401194805 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -27,10 +28,11 @@ public function up(Schema $schema) : void $this->addSql('DROP TABLE wander_image'); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs - $this->addSql('CREATE TABLE wander_image (wander_id INT NOT NULL, image_id INT NOT NULL, INDEX IDX_69C7253A2F82EC6B (wander_id), INDEX IDX_69C7253A3DA5256D (image_id), PRIMARY KEY(wander_id, image_id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB COMMENT = \'\' '); + $this->addSql("CREATE TABLE wander_image (wander_id INT NOT NULL, image_id INT NOT NULL, INDEX IDX_69C7253A2F82EC6B (wander_id), INDEX IDX_69C7253A3DA5256D (image_id), PRIMARY KEY(wander_id, image_id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB COMMENT = '' "); $this->addSQL('INSERT INTO wander_image (wander_id, image_id) SELECT wander_id, id FROM image'); $this->addSql('ALTER TABLE wander_image ADD CONSTRAINT FK_69C7253A2F82EC6B FOREIGN KEY (wander_id) REFERENCES wander (id) ON UPDATE NO ACTION ON DELETE CASCADE'); $this->addSql('ALTER TABLE wander_image ADD CONSTRAINT FK_69C7253A3DA5256D FOREIGN KEY (image_id) REFERENCES image (id) ON UPDATE NO ACTION ON DELETE CASCADE'); diff --git a/src/Migrations/Version20210405202940.php b/src/Migrations/Version20210405202940.php index d23dae57..f1994117 100644 --- a/src/Migrations/Version20210405202940.php +++ b/src/Migrations/Version20210405202940.php @@ -12,6 +12,7 @@ */ final class Version20210405202940 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema) : void $this->addSql('ALTER TABLE wander ADD modified_at DATETIME DEFAULT NULL'); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20210408221450.php b/src/Migrations/Version20210408221450.php index ccb00962..3630295f 100644 --- a/src/Migrations/Version20210408221450.php +++ b/src/Migrations/Version20210408221450.php @@ -12,6 +12,7 @@ */ final class Version20210408221450 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -20,9 +21,10 @@ public function getDescription() : string public function up(Schema $schema) : void { // this up() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE image ADD auto_tags LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:array)\''); + $this->addSql("ALTER TABLE image ADD auto_tags LONGTEXT DEFAULT NULL COMMENT '(DC2Type:array)'"); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20210412170344.php b/src/Migrations/Version20210412170344.php index cae1f516..dcc7afe5 100644 --- a/src/Migrations/Version20210412170344.php +++ b/src/Migrations/Version20210412170344.php @@ -12,6 +12,7 @@ */ final class Version20210412170344 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema) : void $this->addSql('CREATE TABLE problem (id INT AUTO_INCREMENT NOT NULL, created_at DATETIME NOT NULL, description LONGTEXT NOT NULL, uri VARCHAR(255) DEFAULT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20210413195948.php b/src/Migrations/Version20210413195948.php index cba1f716..5bbb8bfc 100644 --- a/src/Migrations/Version20210413195948.php +++ b/src/Migrations/Version20210413195948.php @@ -12,6 +12,7 @@ */ final class Version20210413195948 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema) : void $this->addSql('ALTER TABLE image ADD location VARCHAR(255) DEFAULT NULL'); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20210415185234.php b/src/Migrations/Version20210415185234.php index 6af592b3..5798d0de 100644 --- a/src/Migrations/Version20210415185234.php +++ b/src/Migrations/Version20210415185234.php @@ -12,6 +12,7 @@ */ final class Version20210415185234 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -20,14 +21,16 @@ public function getDescription() : string public function up(Schema $schema) : void { // this up() migration is auto-generated, please modify it to your needs - $this->addSql('CREATE TABLE neighbourhood (id INT AUTO_INCREMENT NOT NULL, bounding_polygon GEOMETRY NOT NULL COMMENT \'(DC2Type:geometry)\', object_id INT NOT NULL, mi_prinx INT NOT NULL, wardcd VARCHAR(2000) NOT NULL, perimeter_m DOUBLE PRECISION NOT NULL, msoa11cd VARCHAR(100) NOT NULL, geo_point2d VARCHAR(200) NOT NULL, area_m2 DOUBLE PRECISION NOT NULL, lsoa11nm VARCHAR(100) NOT NULL, lsoa11cd VARCHAR(100) NOT NULL, lsoa11ln VARCHAR(100) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql("CREATE TABLE neighbourhood (id INT AUTO_INCREMENT NOT NULL, bounding_polygon GEOMETRY NOT NULL COMMENT '(DC2Type:geometry)', object_id INT NOT NULL, mi_prinx INT NOT NULL, wardcd VARCHAR(2000) NOT NULL, perimeter_m DOUBLE PRECISION NOT NULL, msoa11cd VARCHAR(100) NOT NULL, geo_point2d VARCHAR(200) NOT NULL, area_m2 DOUBLE PRECISION NOT NULL, lsoa11nm VARCHAR(100) NOT NULL, lsoa11cd VARCHAR(100) NOT NULL, lsoa11ln VARCHAR(100) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB"); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs $this->addSql('DROP TABLE neighbourhood'); } + public function postUp(Schema $schema): void { // Bristol Open Data: https://opendata.bristol.gov.uk/explore/dataset/lsoa01/export/?location=15,51.44699,-2.62136&basemap=jawg.streets diff --git a/src/Migrations/Version20210423172136.php b/src/Migrations/Version20210423172136.php index 2d5fa1ca..18ce14a3 100644 --- a/src/Migrations/Version20210423172136.php +++ b/src/Migrations/Version20210423172136.php @@ -12,6 +12,7 @@ */ final class Version20210423172136 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema) : void $this->addSql('ALTER TABLE wander DROP modified_at'); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20210424125341.php b/src/Migrations/Version20210424125341.php index aaba9ba0..09e4b2e9 100644 --- a/src/Migrations/Version20210424125341.php +++ b/src/Migrations/Version20210424125341.php @@ -12,6 +12,7 @@ */ final class Version20210424125341 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema) : void $this->addSql('CREATE TABLE tag (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20210424131056.php b/src/Migrations/Version20210424131056.php index 5c87095f..812a8dc6 100644 --- a/src/Migrations/Version20210424131056.php +++ b/src/Migrations/Version20210424131056.php @@ -12,6 +12,7 @@ */ final class Version20210424131056 extends AbstractMigration { + #[\Override] public function getDescription() : string { return ''; @@ -25,11 +26,13 @@ public function up(Schema $schema) : void $this->addSql('ALTER TABLE image_tag ADD CONSTRAINT FK_5B6367D0BAD26311 FOREIGN KEY (tag_id) REFERENCES tag (id) ON DELETE CASCADE'); } + #[\Override] public function down(Schema $schema) : void { // this down() migration is auto-generated, please modify it to your needs $this->addSql('DROP TABLE image_tag'); } + public function postUp(Schema $schema): void { $images = $this->connection->fetchAllAssociative('SELECT id, keywords FROM image'); @@ -39,6 +42,7 @@ public function postUp(Schema $schema): void $keywords = unserialize($image['keywords']); $allKeywords = array_merge($allKeywords, $keywords); } + $unique_keywords = array_unique($allKeywords); $insertTagStmt = $this->connection->prepare('INSERT INTO tag (name) VALUES (:name)'); diff --git a/src/Migrations/Version20210427200452.php b/src/Migrations/Version20210427200452.php index d9ba8a77..f9ac9343 100644 --- a/src/Migrations/Version20210427200452.php +++ b/src/Migrations/Version20210427200452.php @@ -12,6 +12,7 @@ */ final class Version20210427200452 extends AbstractMigration { + #[\Override] public function getDescription(): string { return ''; @@ -23,9 +24,10 @@ public function up(Schema $schema): void $this->addSql('ALTER TABLE image DROP keywords'); } + #[\Override] public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE image ADD keywords LONGTEXT CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_unicode_ci` COMMENT \'(DC2Type:array)\''); + $this->addSql("ALTER TABLE image ADD keywords LONGTEXT CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_unicode_ci` COMMENT '(DC2Type:array)'"); } } diff --git a/src/Migrations/Version20210509222909.php b/src/Migrations/Version20210509222909.php index 6f296db9..af977034 100644 --- a/src/Migrations/Version20210509222909.php +++ b/src/Migrations/Version20210509222909.php @@ -15,6 +15,7 @@ */ final class Version20210509222909 extends AbstractMigration { + #[\Override] public function getDescription(): string { return ''; @@ -26,11 +27,13 @@ public function up(Schema $schema): void $this->addSql('ALTER TABLE wander ADD geo_json LONGTEXT DEFAULT NULL'); } + #[\Override] public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs $this->addSql('ALTER TABLE wander DROP geo_json'); } + // This is handled by Version20211213220450, which adds the google_polyline column and fills it // with data from the GPX files. That's why we don't have the gpxToGeoJson function available // any more; we'll use the polyline from the later migration instead. diff --git a/src/Migrations/Version20210519211549.php b/src/Migrations/Version20210519211549.php index e0c15c5a..5d3d138a 100644 --- a/src/Migrations/Version20210519211549.php +++ b/src/Migrations/Version20210519211549.php @@ -12,6 +12,7 @@ */ final class Version20210519211549 extends AbstractMigration { + #[\Override] public function getDescription(): string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema): void $this->addSql('ALTER TABLE settings ADD twitter_handle VARCHAR(255) DEFAULT NULL'); } + #[\Override] public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20210520201508.php b/src/Migrations/Version20210520201508.php index 3b7299c1..19ffb4e9 100644 --- a/src/Migrations/Version20210520201508.php +++ b/src/Migrations/Version20210520201508.php @@ -12,6 +12,7 @@ */ final class Version20210520201508 extends AbstractMigration { + #[\Override] public function getDescription(): string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema): void $this->addSql('CREATE INDEX ix_wander_start_time ON wander (start_time)'); } + #[\Override] public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20210531094747.php b/src/Migrations/Version20210531094747.php index 7ff7474d..d751e0f9 100644 --- a/src/Migrations/Version20210531094747.php +++ b/src/Migrations/Version20210531094747.php @@ -12,6 +12,7 @@ */ final class Version20210531094747 extends AbstractMigration { + #[\Override] public function getDescription(): string { return ''; @@ -25,6 +26,7 @@ public function up(Schema $schema): void $this->addSql('CREATE UNIQUE INDEX UNIQ_C53D045F5ED948F0 ON image (featuring_wander_id)'); } + #[\Override] public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20210620130007.php b/src/Migrations/Version20210620130007.php index d91675d5..56df6310 100644 --- a/src/Migrations/Version20210620130007.php +++ b/src/Migrations/Version20210620130007.php @@ -12,6 +12,7 @@ */ final class Version20210620130007 extends AbstractMigration { + #[\Override] public function getDescription(): string { return ''; @@ -20,9 +21,10 @@ public function getDescription(): string public function up(Schema $schema): void { // this up() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE image ADD text_tags LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:array)\''); + $this->addSql("ALTER TABLE image ADD text_tags LONGTEXT DEFAULT NULL COMMENT '(DC2Type:array)'"); } + #[\Override] public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20211120120437.php b/src/Migrations/Version20211120120437.php index aed4fc92..787d5ac1 100644 --- a/src/Migrations/Version20211120120437.php +++ b/src/Migrations/Version20211120120437.php @@ -12,6 +12,7 @@ */ final class Version20211120120437 extends AbstractMigration { + #[\Override] public function getDescription(): string { return ''; @@ -27,6 +28,7 @@ public function up(Schema $schema): void } } + #[\Override] public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20211208211945.php b/src/Migrations/Version20211208211945.php index 42c63571..b058747b 100644 --- a/src/Migrations/Version20211208211945.php +++ b/src/Migrations/Version20211208211945.php @@ -12,6 +12,7 @@ */ final class Version20211208211945 extends AbstractMigration { + #[\Override] public function getDescription(): string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema): void $this->addSql('ALTER TABLE settings ADD gravatar_email VARCHAR(1024) DEFAULT NULL'); } + #[\Override] public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20211213220450.php b/src/Migrations/Version20211213220450.php index 523c932b..50c71fc8 100644 --- a/src/Migrations/Version20211213220450.php +++ b/src/Migrations/Version20211213220450.php @@ -21,6 +21,8 @@ public function __construct(Connection $connection, LoggerInterface $logger, pri { parent::__construct($connection, $logger); } + + #[\Override] public function getDescription(): string { return ''; @@ -32,11 +34,13 @@ public function up(Schema $schema): void $this->addSql('ALTER TABLE wander ADD google_polyline LONGTEXT DEFAULT NULL'); } + #[\Override] public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs $this->addSql('ALTER TABLE wander DROP google_polyline'); } + public function postUp(Schema $schema): void { $updateGeoJsonStatement = $this->connection->prepare('UPDATE wander SET google_polyline = (:google_polyline) WHERE id = :id'); @@ -49,6 +53,7 @@ public function postUp(Schema $schema): void if (!$filename) { continue; } + $googlePolyline = $this->gpxService->gpxToGooglePolyline($this->gpxService->getGpxStringFromFilename($filename)); $updateGeoJsonStatement->bindValue('id', $wander->getId()); $updateGeoJsonStatement->bindValue('google_polyline', $googlePolyline); diff --git a/src/Migrations/Version20211221090120.php b/src/Migrations/Version20211221090120.php index d1d937e8..ddc7937b 100644 --- a/src/Migrations/Version20211221090120.php +++ b/src/Migrations/Version20211221090120.php @@ -12,6 +12,7 @@ */ final class Version20211221090120 extends AbstractMigration { + #[\Override] public function getDescription(): string { return ''; @@ -23,6 +24,7 @@ public function up(Schema $schema): void $this->addSql('CREATE INDEX ix_featuring_wander ON image (featuring_wander_id)'); } + #[\Override] public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20211221094902.php b/src/Migrations/Version20211221094902.php index 3bcb9597..28eddd09 100644 --- a/src/Migrations/Version20211221094902.php +++ b/src/Migrations/Version20211221094902.php @@ -12,6 +12,7 @@ */ final class Version20211221094902 extends AbstractMigration { + #[\Override] public function getDescription(): string { return ''; @@ -24,6 +25,7 @@ public function up(Schema $schema): void $this->addSql('ALTER TABLE wander DROP geo_json'); } + #[\Override] public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20260201203811.php b/src/Migrations/Version20260201203811.php index 0d7cb500..2836bb55 100644 --- a/src/Migrations/Version20260201203811.php +++ b/src/Migrations/Version20260201203811.php @@ -12,6 +12,7 @@ */ final class Version20260201203811 extends AbstractMigration { + #[\Override] public function getDescription(): string { return ''; @@ -26,6 +27,7 @@ public function up(Schema $schema): void $this->addSql('CREATE INDEX IDX_75EA56E0E3BD61CE ON messenger_messages (available_at)'); } + #[\Override] public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20260201215733.php b/src/Migrations/Version20260201215733.php index 4f322967..b282f436 100644 --- a/src/Migrations/Version20260201215733.php +++ b/src/Migrations/Version20260201215733.php @@ -12,6 +12,7 @@ */ final class Version20260201215733 extends AbstractMigration { + #[\Override] public function getDescription(): string { return 'Rename image.location to image.neighbourhood'; @@ -23,6 +24,7 @@ public function up(Schema $schema): void $this->addSql('ALTER TABLE image rename column location to neighbourhood'); } + #[\Override] public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20260301192810.php b/src/Migrations/Version20260301192810.php index 9cf04360..53b4019b 100644 --- a/src/Migrations/Version20260301192810.php +++ b/src/Migrations/Version20260301192810.php @@ -12,6 +12,7 @@ */ final class Version20260301192810 extends AbstractMigration { + #[\Override] public function getDescription(): string { return ''; @@ -23,12 +24,13 @@ public function up(Schema $schema): void $this->addSql('DROP INDEX IDX_75EA56E0E3BD61CE ON messenger_messages'); $this->addSql('DROP INDEX IDX_75EA56E016BA31DB ON messenger_messages'); $this->addSql('DROP INDEX IDX_75EA56E0FB7336F0 ON messenger_messages'); - $this->addSql('ALTER TABLE messenger_messages CHANGE created_at created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', CHANGE available_at available_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', CHANGE delivered_at delivered_at DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\''); + $this->addSql("ALTER TABLE messenger_messages CHANGE created_at created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', CHANGE available_at available_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', CHANGE delivered_at delivered_at DATETIME DEFAULT NULL COMMENT '(DC2Type:datetime_immutable)'"); $this->addSql('CREATE INDEX IDX_75EA56E0FB7336F0E3BD61CE16BA31DBBF396750 ON messenger_messages (queue_name, available_at, delivered_at, id)'); $this->addSql('ALTER TABLE sessions CHANGE sess_data sess_data LONGBLOB NOT NULL'); $this->addSql('CREATE INDEX sess_lifetime_idx ON sessions (sess_lifetime)'); } + #[\Override] public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Migrations/Version20260301200336.php b/src/Migrations/Version20260301200336.php index 378b64a1..1a15708f 100644 --- a/src/Migrations/Version20260301200336.php +++ b/src/Migrations/Version20260301200336.php @@ -12,6 +12,7 @@ */ final class Version20260301200336 extends AbstractMigration { + #[\Override] public function getDescription(): string { return ''; @@ -26,6 +27,7 @@ public function up(Schema $schema): void $this->addSql("UPDATE image SET copyright = CONCAT('Copyright © Matt Gibson ', YEAR(captured_at));"); } + #[\Override] public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs diff --git a/src/Repository/ProblemRepository.php b/src/Repository/ProblemRepository.php index 4ddb0621..1d01dc57 100644 --- a/src/Repository/ProblemRepository.php +++ b/src/Repository/ProblemRepository.php @@ -1,5 +1,7 @@ getMessage()); + throw new HttpException(500, "Failed finishing GPX upload: " . $fileException->getMessage(), $fileException); } return $newFilename; diff --git a/src/Twig/GeneralExtension.php b/src/Twig/GeneralExtension.php index 8115bccf..992268b1 100644 --- a/src/Twig/GeneralExtension.php +++ b/src/Twig/GeneralExtension.php @@ -1,5 +1,7 @@ exif->getRawData(); if (array_key_exists('IPTC:CopyrightNotice', $raw)) { $copyright = $raw['IPTC:CopyrightNotice']; - if (is_string($copyright) && !empty($copyright)) { + if (is_string($copyright) && ($copyright !== '' && $copyright !== '0')) { return $copyright; } } diff --git a/src/Utils/ExifHelperInterface.php b/src/Utils/ExifHelperInterface.php index 905d0936..8f2612c7 100644 --- a/src/Utils/ExifHelperInterface.php +++ b/src/Utils/ExifHelperInterface.php @@ -1,5 +1,7 @@ getTags()->current(); - assert($image2tagTwo instanceof Tag); - $this->assertEquals('two', $image2tagTwo->getName(), "Unexpected tag name found in image2"); + $this->assertInstanceOf(Tag::class, $image2tagTwo); + $this->assertSame('two', $image2tagTwo->getName(), "Unexpected tag name found in image2"); // ...but it should also be the *same* tag as was used // for "two" in image1, so the same database id should // exist in image2's tags. $this->assertTrue($image1->getTags()->exists(function ($k, $v) use ($image2tagTwo) { - assert($v instanceof Tag); + $this->assertInstanceOf(Tag::class, $v); return $v->getId() === $image2tagTwo->getId(); })); } diff --git a/tests/symfony-container.php b/tests/symfony-container.php index 1a37a1ed..7fa25d41 100644 --- a/tests/symfony-container.php +++ b/tests/symfony-container.php @@ -1,4 +1,6 @@ Date: Sun, 2 Aug 2026 23:57:24 +0100 Subject: [PATCH 13/14] Fix minor SonarQube warning in ImageTagTest --- tests/ImageTagTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageTagTest.php b/tests/ImageTagTest.php index 689cb279..fc6be28d 100644 --- a/tests/ImageTagTest.php +++ b/tests/ImageTagTest.php @@ -331,7 +331,7 @@ public function testSaveTagsAvoidsDuplication(): void // ...but it should also be the *same* tag as was used // for "two" in image1, so the same database id should // exist in image2's tags. - $this->assertTrue($image1->getTags()->exists(function ($k, $v) use ($image2tagTwo) { + $this->assertTrue($image1->getTags()->exists(function ($_, $v) use ($image2tagTwo) { $this->assertInstanceOf(Tag::class, $v); return $v->getId() === $image2tagTwo->getId(); })); From ec3d8e275c206d0782f900037685f34fd91a2d6b Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Sun, 2 Aug 2026 23:57:38 +0100 Subject: [PATCH 14/14] Disable yarn install lifecycle scripts in CI SonarQube flagged that lifecycle scripts (preinstall/postinstall) run by default, a known supply-chain attack vector. Add --ignore-scripts; verified locally that install and yarn build both still succeed since none of our JS deps need a native build/install step. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 503dce34..c4ee6f82 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -110,7 +110,7 @@ jobs: cache: yarn - name: Install JS dependencies - run: yarn install --frozen-lockfile + run: yarn install --frozen-lockfile --ignore-scripts # base.html.twig calls encore_entry_link_tags()/encore_entry_script_tags(), # which need public/build/entrypoints.json (git-ignored, Webpack Encore