AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Pimcore 12 Upgrade

skill-cors-gmbh-pimcore-skills-pimcore-12-upgrade · by cors-gmbh

Upgrade Pimcore 11 projects and bundles to Pimcore 12 - composer, config, PHP 8.3, Symfony 7, Docker, CI/CD, cors/dev, cors/saml

No reviews yet
0 installs
0 views
view→install

Install

$ agentstack add skill-cors-gmbh-pimcore-skills-pimcore-12-upgrade

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution Used

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-cors-gmbh-pimcore-skills-pimcore-12-upgrade)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
5mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Pimcore 12 Upgrade? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Pimcore 11 to Pimcore 12 Upgrade

You are helping upgrade a Pimcore 11 codebase to Pimcore 12. First determine whether this is a Project or a Bundle, as the upgrade steps differ.

Step 0: Determine Project vs Bundle

Project = A full Pimcore application (has bin/console, config/, public/index.php, docker-compose.yaml). Bundle = A reusable Symfony/Pimcore bundle (has no bin/console, extends AbstractPimcoreBundle, published as composer package).

Check for indicators:

# Project indicators
ls bin/console config/bundles.php public/index.php docker-compose.yaml 2>/dev/null

# Bundle indicators
grep -r "AbstractPimcoreBundle\|AbstractBundle" src/ --include="*.php" -l

Part A: Upgrade Steps for PROJECTS

A1: Update PHP Requirement

Pimcore 12 requires PHP 8.3+.

composer.json:

"php": ">=8.3"

Docker: Update PHP image to 8.3 or 8.4.

A2: Update composer.json Dependencies

composer require pimcore/pimcore:^12.0
composer require pimcore/admin-ui-classic-bundle:^2.0

If using CORS packages:

composer require cors/dev:12.x-dev --dev
composer require cors/saml:12.x-dev  # if SAML is used
composer require cors/cors:^0.3.0    # if cors-bundle is used, check latest version

If using CoreShop packages (individual or full suite):

# CoreShop v4 → v5 (all coreshop/* packages)
# For x-dev packages: 4.1.x-dev → 5.0.x-dev
# For stable constraints: ^4.1 → ^5.0
composer require coreshop/theme-bundle:5.0.x-dev  # if used
composer require coreshop/menu-bundle:5.0.x-dev   # if used
composer require coreshop/messenger-bundle:5.0.x-dev  # if used
composer require coreshop/registry:^5.0            # if used

Symfony version support changes:

"symfony/dotenv": "^6.4 | ^7.4",
"symfony/runtime": "^6.4 | ^7.4"

A3: Pimcore License Configuration

Create config/license.yaml:

pimcore:
    encryption:
        secret: '%env(PIMCORE_ENCRYPTION_SECRET)%'
    product_registration:
        instance_identifier: '%env(PIMCORE_INSTANCE_IDENTIFIER)%'
        product_key: '%env(PIMCORE_PRODUCT_KEY)%'

Import it in config/config.yaml:

imports:
    - { resource: license.yaml }

Add to .env (or .env.local):

PIMCORE_ENCRYPTION_SECRET=your_secret_here
PIMCORE_INSTANCE_IDENTIFIER=your_instance_id_here
PIMCORE_PRODUCT_KEY=your_product_key_here

Important: PIMCORE_ENCRYPTION_SECRET must not be empty, otherwise you get:

`pimcore.encryption.secret` is not set.
Run `vendor/bin/generate-defuse-key` to generate a secret and set it as container parameter `pimcore.encryption.secret`.

Generate the secret with:

docker compose exec php vendor/bin/generate-defuse-key

Then set the output as PIMCORE_ENCRYPTION_SECRET in .env.local.

A4: Docker Compose Updates

Pimcore 12 introduces new required services. Update docker-compose.yaml:

Add these services:

  • mercure - Real-time notifications (required for Pimcore Studio)
  • opensearch:2.13.0 - Search engine (replaces Elasticsearch for Generic Data Index)
  • opensearch-dashboards:2.13.0 - OpenSearch UI (optional, for debugging)
  • rabbitmq:3-management - Message queue

Remove if present (enterprise-only):

  • gotenberg
  • pdfreactor

If using cors/dev, update docker-compose include:

include:
  - vendor/cors/dev/docker-compose-dev.yaml

A4b: Update .docker/composer-setup.sh

If the project has a .docker/composer-setup.sh, update the Docker image registry from the old GitLab registry to GitHub Container Registry:

Before (Pimcore 11):

docker run --rm --env COMPOSER_AUTH="$COMPOSER_AUTH" --volume "$(pwd)":/var/www/html:cached git.e-conomix.at:5050/cors/docker/php-alpine-${DOCKER_ALPINE_VERSION}-cli:${DOCKER_PHP_VERSION}-${DOCKER_BASE_IMAGE} composer install --no-scripts --no-interaction

After (Pimcore 12):

docker run --rm --env COMPOSER_AUTH="$COMPOSER_AUTH" --volume "$(pwd)":/var/www/html:cached ghcr.io/cors-gmbh/pimcore-docker/php-fpm:${DOCKER_PHP_VERSION}-alpine${DOCKER_ALPINE_VERSION}-${DOCKER_BASE_IMAGE} composer install --no-scripts --no-interaction

Key changes:

  • Registry: git.e-conomix.at:5050/cors/docker/ghcr.io/cors-gmbh/pimcore-docker/
  • Image name format: php-alpine-{ALPINE}-cli:{PHP}-{BASE}php-fpm:{PHP}-alpine{ALPINE}-{BASE}

A5: Update Static Analysis & Code Quality Configs

Replace local configs with shared cors/dev imports:

ecs.php:

import('vendor/cors/dev/ecs.php');
    $ecsConfig->parallel();
    $ecsConfig->paths(['src']);
};

phpstan.neon:

includes:
    - vendor/cors/dev/phpstan.neon

parameters:
    paths:
        - src

psalm.xml:


    
    
        
        
            
        
    

A6: Update CI/CD Pipeline

Update .gitlab-ci.yml to reference the correct cors/docker version:

include:
  - project: 'cors/docker'
    ref: "8.0"
    file: '.project-gitlab-ci.yml'

A7: Migrate Bundles from Kernel.php to config/bundles.php

Pimcore 11 projects often register bundles in src/Kernel.php via registerBundlesToCollection(). In Pimcore 12, migrate all bundles to config/bundles.php instead.

Before (Pimcore 11 - src/Kernel.php):

class Kernel extends PimcoreKernel
{
    public function registerBundlesToCollection(BundleCollection $collection): void
    {
        $collection->addBundle(new SentryBundle(), 0, ['staging', 'prod']);
        $collection->addBundle(new CORSBundle());
        $collection->addBundle(new PimcoreAdminBundle(), 60);
        $collection->addBundle(new CoreShopCoreBundle(), 40);
    }
}

After (Pimcore 12 - config/bundles.php):

 ['all' => true],
    Sentry\SentryBundle\SentryBundle::class => ['staging' => true, 'prod' => true],
    CORS\Bundle\CORSBundle\CORSBundle::class => ['all' => true],
    CoreShop\Bundle\CoreBundle\CoreShopCoreBundle::class => ['all' => true],
];

After (Pimcore 12 - src/Kernel.php):

class Kernel extends PimcoreKernel
{
}

Migration rules:

  • Bundles with no environment restriction → ['all' => true]
  • Bundles with ['staging', 'prod']['staging' => true, 'prod' => true]
  • Priority parameter from addBundle() is no longer needed (Symfony handles order via bundles.php)
  • Remove all use imports and the registerBundlesToCollection method from Kernel.php
  • Remove bundles for packages that were removed during the upgrade

A8: Migrate Annotations to Attributes

Symfony 7 removed support for annotation route loaders. Update config/routes.yaml:

Before:

app:
    resource: "../src/Controller/"
    type: annotation

After:

app:
    resource: "../src/Controller/"
    type: attribute

Also update config/services.yaml if it references the old Symfony\Component\Security\Core\Security class (removed in Symfony 7):

Before:

- '@Symfony\Component\Security\Core\Security'

After:

- '@Symfony\Bundle\SecurityBundle\Security'

A8b: Remove Deprecated Security Config Options

Symfony 7 removed enable_authenticator_manager (it's now always enabled). Remove it from config/packages/security.yaml:

Before:

security:
    enable_authenticator_manager: true

After:

security:

A8c: Fix Serializer Interface Changes

Symfony 7 enforces strict signatures on NormalizerInterface. Update all custom normalizers:

normalize() method:

// Before (Symfony 6):
public function normalize($object, string $format = null, array $context = [])

// After (Symfony 7):
public function normalize(mixed $data, ?string $format = null, array $context = []): \ArrayObject|array|string|int|float|bool|null

supportsNormalization() method:

// Before (Symfony 6):
public function supportsNormalization($data, $format = null)

// After (Symfony 7):
public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool

New required method getSupportedTypes():

public function getSupportedTypes(?string $format): array
{
    return [
        MyClass::class => true,
    ];
}

A9: Pimcore Studio API Route Changes

If you have custom admin controllers, note the API prefix change:

  • Pimcore 11: /admin/...
  • Pimcore 12: /pimcore-studio/api/...

A10: Deprecation Cleanup

Check for deprecations:

bin/console debug:container --deprecations
grep -rn "StaticRoutesBundle" config/ src/  # deprecated since 12.3

A11: Clean Up and Run Migrations (LAST STEP)

Important: Only run migrations after everything else works (cache:clear, manual browser test).

  1. Delete all existing app migrations in src/Migrations/ - they reference removed bundles and use deprecated ContainerAwareTrait (removed in Symfony 7).
  1. Create a cleanup migration that removes old entries from the migration_versions table:
final class VersionXXXX extends AbstractMigration
{
    public function getDescription(): string
    {
        return 'Pimcore 12 upgrade: clean up old app migrations from migration_versions table';
    }

    public function up(Schema $schema): void
    {
        $this->addSql("DELETE FROM migration_versions WHERE version LIKE 'App\\\\Migrations\\\\%' AND version != 'App\\\\Migrations\\\\VersionXXXX'");
    }

    public function down(Schema $schema): void
    {
    }
}
  1. Run migrations (Pimcore's own migrations handle data conversions like serialized PHP to JSON):
docker compose exec php bin/console doctrine:migrations:migrate --no-interaction

Before running migrations, verify:

  • docker compose exec php bin/console cache:clear works without errors
  • Manual test in browser shows no critical errors
  • Ask the user for confirmation before executing

Part B: Upgrade Steps for BUNDLES

Bundles follow a simpler process because they don't manage Docker, database, or application config directly.

B1: Update composer.json

{
  "require": {
    "php": ">=8.3",
    "pimcore/pimcore": "^11.0 | ^12.0"
  },
  "require-dev": {
    "cors/dev": "12.x-dev",
    "pimcore/admin-ui-classic-bundle": "^2.0"
  }
}

Key points:

  • Support both ^11.0 | ^12.0 if possible for backwards compatibility
  • cors/dev goes to 12.x-dev (not dev-main)
  • pimcore/admin-ui-classic-bundle bumps to ^2.0
  • Add Symfony 7 support: "symfony/*": "^6.4 | ^7.4"

B2: Update CI/CD Pipeline

Bundle pipelines use a different CI template than projects:

include:
  - project: 'cors/docker'
    ref: "8.0"
    file: '.bundle-gitlab-ci.yml'

Note: .bundle-gitlab-ci.yml (not .project-gitlab-ci.yml).

B3: Update Static Analysis Configs

Same as project (Step A5) - use shared cors/dev imports for ecs.php, phpstan.neon, psalm.xml.

Important for psalm.xml: Make sure ` is defined locally (not just inherited via xi:include), otherwise vendor/bin/psalm` without arguments won't analyze any files. See Step A5 for the correct config.

B4: Add #[\Override] Attributes

PHP 8.3 introduces #[\Override]. Add it to all methods that override a parent class or implement an interface method:

final class MyBundle extends AbstractPimcoreBundle
{
    #[\Override]
    public function getNiceName(): string { return 'My Bundle'; }

    #[\Override]
    public function getDescription(): string { return 'Description'; }
}

Common methods that need #[\Override]:

  • getNiceName(), getDescription() on bundle classes
  • load() on DependencyInjection Extension classes
  • getConfigTreeBuilder() on Configuration classes
  • getSubscribedEvents() on EventSubscriber classes
  • getType() on Document Editable classes
  • compile() on Twig Node classes
  • parse(), getTag() on Twig TokenParser classes
  • getTokenParsers() on Twig Extension classes

B4b: Fix Symfony 7 Return Type Declarations

Symfony 7 enforces strict return type declarations. Methods that previously had no return type now require one:

Command execute() method:

// Before (Pimcore 11 / Symfony 6):
protected function execute(InputInterface $input, OutputInterface $output)

// After (Pimcore 12 / Symfony 7):
protected function execute(InputInterface $input, OutputInterface $output): int

Search all Command classes and add : int return type to execute():

grep -rl "protected function execute(InputInterface \$input, OutputInterface \$output)$" src/ --include="*.php"

B5: Add final to Classes

Make classes final unless they are explicitly designed for extension:

final class MyListener implements EventSubscriberInterface { ... }
final class MyExtension extends Extension { ... }
final class MyController extends UserAwareController { ... }

B6: Add declare(strict_types=1)

Every PHP file must have declare(strict_types=1); after the opening 0)

  • UnusedClass / PossiblyUnusedMethod → suppress in psalm.xml (false positives for DI-based bundles)
  • MissingConstructor from vendor code → suppress in psalm.xml

Suppress bundle-typical false positives in psalm.xml:


    
    
    
    

Quick Reference: Version Matrix

| Dependency | Pimcore 11 | Pimcore 12 | |---|---|---| | PHP | >=8.1 | >=8.3 | | Symfony | ^6.4 | ^6.4 \| ^7.4 | | pimcore/pimcore | ^11.0 | ^12.0 | | pimcore/admin-ui-classic-bundle | ^1.x | ^2.0 | | pimcore optional bundles (file-explorer, google-marketing, newsletter, system-info, web-to-print) | ^1.x | ^2.0 | | pimcore/ecommerce-framework-bundle | ^1.0 | removed (no P12 version) | | pimcore/personalization-bundle | ^1.0 | removed (no P12 version) | | coreshop/* (core-shop, theme-bundle, menu-bundle, messenger-bundle, registry, etc.) | 4.x / 4.1.x-dev | ^5.0 / 5.0.x-dev | | cors/dev | dev-main / ^1.0@dev | 12.x-dev | | cors/cors | ^0.2 | ^0.3.0 | | cors/saml | ^11.0 | 12.x-dev | | cors/web-care | ^11.0 | ^2.0 | | cors/prometheus | ^11.0 | ^2.0 | | cors/docker CI ref | 7.0 | 8.0 | | phpstan/phpstan | ^1.10 | ^1.10 \|\| ^2.0 | | vimeo/psalm | ^5.0 | ^5.0 \|\| ^6.0 | | symfony/webpack-encore-bundle | ^1.17 | ^1.17 \| ^2.0 |

Packages with NO Pimcore 12 Support (as of Feb 2026)

These packages must be removed when upgrading to Pimcore 12:

| Package | Notes | |---|---| | dachcom-digital/dynamic-search | Requires pimcore ^11.0 | | dachcom-digital/dynamic-search-data-provider-trinity | Requires pimcore ^11.0 | | dachcom-digital/dynamic-search-index-provider-elasticsearch | Requires pimcore ^11.0 | | dachcom-digital/emailizr | Requires pimcore ^11.0 | | dachcom-digital/seo | Requires pimcore ^11.0 | | cors/kubernetes-console | Requires admin-ui-classic-bundle ^1.0 |

Project require-dev Cleanup

When using cors/dev:12.x-dev, the following packages are bundled and should be removed from project-level require-dev:

  • phpstan/phpstan
  • phpstan/phpstan-symfony
  • symplify/easy-coding-standard
  • vimeo/psalm

Replacing Removed Dachcom Bundles

dachcom-digital/emailizr

The emailizr bundle provides CSS inlining for email templates via custom Twig tags ({% emailizr_inline_style %}, {% end_emailizr_inline_style %}), a emailizr_style_collector global, and an emailizr_inline_style() Twig function.

Steps to replace:

  1. Install required dependencies:
composer require pelago/emogrifier twig/inky-extra
  1. Create the following classes in src/Emailizr/:

src/Emailizr/Collector/CssCollector.php - Collects CSS file paths for inlining:


 */
class CssCollector implements \IteratorAggregate
{
    /** @var list */
    protected array $cssFiles = [];

    public function add(string $file): void
    {
        $this->cssFiles[] = $file;
    }

    public function removeAll(): void
    {
        $this->cssFiles = [];
    }

    /**
     * @return \ArrayIterator, string>
     */
    #[\Override]
    public function getIterator(): \Arr

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [cors-gmbh](https://github.com/cors-gmbh)
- **Source:** [cors-gmbh/pimcore-skills](https://github.com/cors-gmbh/pimcore-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.