Skip to content

Commit

Permalink
Improvements for Notable changes for PS9
Browse files Browse the repository at this point in the history
  • Loading branch information
kpodemski committed Jul 31, 2024
1 parent 3ea92c2 commit a08d8bb
Showing 1 changed file with 83 additions and 79 deletions.
162 changes: 83 additions & 79 deletions modules/core-updates/9.0.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
title: Changes in PrestaShop 9.0
menuTitle: Changes in 9.0
useMermaid: true
---

<style>
Expand All @@ -13,20 +14,23 @@ menuTitle: Changes in 9.0

# Notable changes in PrestaShop 9.0

This section provides a list of the most significant changes in PrestaShop 9.0. While this list is not exhaustive, it aims to give developers a clear overview of the important updates. If you notice any missing or incorrect information, please help us improve by creating an issue on our [GitHub repository](https://github.com/PrestaShop/docs/issues/new).

## PHP support

PrestaShop 9.0 now requires **PHP 8.1 minimum**, with support added for PHP 8.2 and 8.3.

- [Remove compatibility for php versions inferior to PHP 8.1](https://github.com/PrestaShop/PrestaShop/pull/32514)
- [Remove compatibility for PHP versions inferior to PHP 8.1](https://github.com/PrestaShop/PrestaShop/pull/32514)
- [Support for PHP 8.2](https://github.com/PrestaShop/PrestaShop/pull/29116)
- [Support for PHP 8.3](https://github.com/PrestaShop/PrestaShop/pull/34485)

## Core changes

### Symfony Upgrade

PrestaShop V9 has been upgraded to rely on Symfony 6.4, V8 was previously based on Symfony 4.4 so it's quite a big leap. As such the V9 includes all the breaking changes related to the Symfony Framework, you can see [more details about the changes in the core here](https://github.com/PrestaShop/PrestaShop/issues/33993) and if you can also check Symfony migration guides (see below).
Symfony 6.4 is the latest LTS version and will be supported until November 2026 for bug fixes, and November 2027 for security fixes.
PrestaShop 9.0 has been upgraded to rely on Symfony 6.4. This is a significant leap from the previous version, 8.x, which was based on Symfony 4.4. As a result, version 9 includes all the breaking changes related to the Symfony Framework. For more details about these changes in the core, you can refer to the [PrestaShop GitHub issue](https://github.com/PrestaShop/PrestaShop/issues/33993). Additionally, you can consult the Symfony migration guides (see links below).

Symfony 6.4 is the latest LTS version and will receive bug fixes until November 2026, as well as security fixes until November 2027.

- [UPGRADE FROM 4.4 to 5.0](https://github.com/symfony/symfony/blob/5.4/UPGRADE-5.0.md)
- [UPGRADE FROM 5.0 to 5.1](https://github.com/symfony/symfony/blob/5.4/UPGRADE-5.1.md)
Expand All @@ -42,48 +46,57 @@ Symfony 6.4 is the latest LTS version and will be supported until November 2026

### Removed dependencies

We followed Symfony recommendations and no longer use the `symfony/symfony` dependency but instead each sub package manually, the minimum version of PHP 8.1 also forced us to upgrade or removed some dependencies.
We used this occasion to clean the dependencies that are no longer used in the core (whether they are from symfony or not), if you need these dependencies in your modules you will have to integrate them in your module's dependencies.

- `guzzlehttp/guzzle` (replaced by Symfony HTTP client in the core)
- `league/tactician-bundle` (replaced by Symfony Messenger component)
- `pear/archive_tar`
- `sensio/framework-extra-bundle` (we now favor annotations in the core)
- `soundasleep/html2text`
- `swiftmailer/swiftmailer` (replaced by Symfony Mailer component in the core)
- `symfony/inflector`
- `symfony/notifier`
- `symfony/rate-limiter`
- `symfony/semaphore`
- `symfony/uid`
- `symfony/workflow`
We have followed Symfony's recommendations and made changes to our dependencies. Instead of using the `symfony/symfony` dependency, we now manually include each sub-package. Additionally, due to the minimum PHP version requirement of 8.1, we had to upgrade or remove certain dependencies.

During this process, we took the opportunity to clean up dependencies that are no longer used in the core, regardless of whether they are from Symfony or not. If you rely on these dependencies in your modules, you will need to integrate them into your module's dependencies.

| Library name | Reason for removal/Replacement |
|------------------------------------|-------------------------------------------------------------|
| guzzlehttp/guzzle | Replaced by Symfony HTTP client in the core |
| league/tactician-bundle | Replaced by Symfony Messenger component |
| pear/archive_tar | No longer used |
| sensio/framework-extra-bundle | We now favor annotations in the core |
| soundasleep/html2text | No longer used |
| swiftmailer/swiftmailer | Replaced by Symfony Mailer component in the core |
| symfony/inflector | No longer used |
| symfony/notifier | No longer used |
| symfony/rate-limiter | No longer used |
| symfony/semaphore | No longer used |
| symfony/uid | No longer used |
| symfony/workflow | No longer used |

#### Mailer Migration

We have migrated from Swift Mailer to Symfony Mailer. This change includes several improvements and aligns with Symfony's latest recommendations.

{{% notice note %}}
**Mail SSL encryption was dropped**

When we migrated from Swift Mailer to Symfony Mailer, we noticed that SSL support was not an option in ESMTP transport. SSL is an old and outdated encryption type, and for security reasons, it will no longer be allowed.

The remaining choices are "TLS encryption" or "No encryption".
{{% /notice %}}



### Upgraded dependencies

Some dependencies are still present but were upgraded which comes with their own breaking changes, please refer to each dependency changelog to understand them in details if you depend on these dependencies:

- `api-platform/core`: `2.7.6` -> `3.2.13`
- `composer/installers`: `1.12.0` -> `2.2.0`
- `friendsofsymfony/jsrouting-bundle`: `2.8.0` -> `3.2.1`
- `lcobucci/jwt`: `3.4.6` (special patch from https://github.com/PrestaShop/jwt.git) -> `5.0.0` (no need for fork version anymore)
- `mobiledetect/mobiledetectlib`: `2.8.41` -> `3.74.0`
- `pelago/emogrifier`: `5.0.1` -> `7.0.0`
- Symfony dependencies all upgraded from `4.4` to `6.4`
- `twig/twig`: `3.4.3` -> `3.8.0`
- `doctrine/dbal`: `2.13.8` -> `3.6.5`
- `doctrine/lexer`: `1.2.3` -> `2.1.1`
- `doctrine/orm`: `2.12.1` -> `2.15.5`
- `doctrine/deprecations`: `0.5.3` -> `1.1.3`
- 'egulias/email-validator': `3.2.6` -> `4.0.1`
| Name | Old version | New version |
|------------------------------------|---------------------------------------------------------------|---------------|
| api-platform/core | 2.7.6 | 3.2.13 |
| composer/installers | 1.12.0 | 2.2.0 |
| friendsofsymfony/jsrouting-bundle | 2.8.0 | 3.2.1 |
| lcobucci/jwt | 3.4.6 (special patch from https://github.com/PrestaShop/jwt.git) | 5.0.0 (no need for fork version anymore) |
| mobiledetect/mobiledetectlib | 2.8.41 | 3.74.0 |
| pelago/emogrifier | 5.0.1 | 7.0.0 |
| Symfony dependencies | 4.4 | 6.4 |
| twig/twig | 3.4.3 | 3.8.0 |
| doctrine/dbal | 2.13.8 | 3.6.5 |
| doctrine/lexer | 1.2.3 | 2.1.1 |
| doctrine/orm | 2.12.1 | 2.15.5 |
| doctrine/deprecations | 0.5.3 | 1.1.3 |
| egulias/email-validator | 3.2.6 | 4.0.1 |


### Symfony controllers

Expand Down Expand Up @@ -182,27 +195,29 @@ The `PrestaShopAdminController` class comes with other helpful helper methods th
### Kernel modifications

Until PrestaShop 8.0, we relied on one Kernel used for the back office. In Symfony 9.0, we expanded the usage of the Symfony framework, especially for two new features:
- the new Admin API based on API Platform and OAuth authentication
- [the new Admin API based on API Platform](https://build.prestashop-project.org/news/2024/meet-prestashop9-api/) and OAuth authentication
- an experimental feature that gives access to a Symfony container in the front office (in opposition to the custom-built container available today). This one is not very much used yet, but it's a first stone for the future front office migration

Those two new environments have mechanisms and configuration different from those in the back office. To separate these configurations cleanly, they each have their dedicated kernel class and their own configuration. This allows to cleanly define different routing, security configurations, dedicated services, listeners, ...
Those two new environments have mechanisms and configuration different from those in the back office. To separate these configurations cleanly, they each have their dedicated kernel class and their own configuration. This allows to cleanly define different routing, security configurations, dedicated services, listeners, etc.

Each kernel has a unique `applicationId` that allows to dynamize its configuration and cache folder (since the services are not the same, each kernel needs its own cache folder). The `appId` parameter has also been added globally to the `bin/console` tool. They all share the common `app/config/config.yml` configuration file, but each one extends it in its own config folder.

| Environment | Kernel class | Config folder | Cache folder | Endpoint | Application ID |
| Environment | Kernel class | Config folder | Cache folder | Endpoint | App ID |
|-----------------------------|------------------|------------------------|---------------------------------------|------------------------|----------------|
| Back office | `AdminKernel` | `app/config/admin` | `var/cache/{dev,prod,test}/admin` | `/admin-dev/index.php` | `admin` |
| Admin API | `AdminAPIKernel` | `app/config/admin-api` | `var/cache/{dev,prod,test}/admin-api` | `/admin-api/index.php` | `admin-api` |
| Front office (experimental) | `FrontKernel` | `app/config/front` | `var/cache/{dev,prod,test}/front` | `/index.php` | `front` |

{{% notice note %}}
**Console usage**
**Console usage examples**

Clear the cache of the Admin API for its `prod` environment
To clear the cache of the Admin API for its `prod` environment:

`php bin/console cache:clear --env=prod --app-id=admin-api`

Display event listeners for the back office in `dev` environment (default value of `app-id` is `admin` for retro compatibility)
--

To display event listeners for the back office in `dev` environment (default value of `app-id` is `admin` for retro compatibility):
`php bin/console debug:event-dispatcher kernel.request --env=dev`
{{% /notice %}}

Expand All @@ -217,13 +232,13 @@ All the back office pages share a common layout, composed of a few elements:
- the `<head>` element that includes all the CSS and JS (among other things)
- the side navigation menu
- the header of the page which is itself composed of:
- quick accesses
- quick access component
- search form
- notifications center
- employee dropdown
- the multistore header (when multistore is enabled)
- the page toolbar (which includes breadcrumb and top action buttons)
- the footer (which only contains a displayBackOfficeFooter display hook)
- the page toolbar (which includes breadcrumbs and top action buttons)
- the footer (which only contains a `displayBackOfficeFooter` display hook)

Until PrestaShop 8.1, all these common elements were handled by legacy code, so even on the migrated pages, there was always a background legacy controller based on `AdminController` in charge of building the layout data and rendering it. Once the layout was rendered the central content of Symfony pages was included in the middle of it. It means no page was completely free from the legacy controllers, which would ultimately block the end of migration, while they are intended to disappear completely.

Expand All @@ -234,53 +249,42 @@ On legacy pages, we follow the same principle. Symfony is now in charge of rende
**What changes for my module pages?**

We refactored this layout with maximum backward compatibility in mind. The HTML layout itself had minimum changes (in both migrated and legacy pages). We tried to keep the same hooks in both the PHP code and the Twig templates. We even introduced a fake legacy controller in migrated pages that has no logic but is kept mostly as a [DTO](https://en.wikipedia.org/wiki/Data_transfer_object) to contain things like CSS files and JS files, because that's where most modules add their content.

We still had to change the controller workflow. Many of the functions in `AdminController` are no longer useful as they are used to initialize the layout variables (now handled by Twig components). Some methods also render or write output directly, which we prevent as we need to get the rendered content as a string to integrate it correctly inside the new layout. This means some internal methods had to be split to avoid unwanted usage.

**Legacy workflow:**

- `Dispatcher::dispatch` would detect the legacy controller and instantiate it
- `AdminController::run` was executed by the `Dispatcher`, which was split into
- `AdminController::init`
- `AdminController::checkAccess` would handle the check of the legacy token in the url
- `AdminController::setMedia` is in charge of defining the JS/CSS files to be loaded in the page
- `AdminController::postProcess` processes the posted data
- if after this method `AdminController::redirect_after` is defined then perform an HTTP redirection
- `AdminController::initHeader`
- `AdminController::initContent`
- `AdminController::initFooter`
- Check if `AdminController::ajax`, one of the two is executed
- `AdminController::displayAjax` (or specific `displayAjax{action}` method)
- `AdminController::display`
- init many variables used by the layout templates (meta title, toolbar, JS definitions, ...)
- render `page`, `header`, `footer` and sets them as Smarty variables
- fetch the Smarty `AdminController::template` to get the content of the page
- calls `smartyOutputContent` to echo/output the whole page (layout + content)

Here you'll find the [execution workflow of legacy controllers]({{< relref "9/development/architecture/legacy/legacy-controllers.md" >}})

**Symfony workflow:**

- A special routing matching URLs like `?controller=LegacyAdminController` checks if the URL is valid thanks to `LegacyRouterChecker::check`
- the query parameter must match an existing legacy controller class
- the controller object is instantiated and `AdminController::init` is called, this is important to know if the controller is anonymous, this is the proper moment to define `AdminController::allowAnonymous`
- a few attributes are set on the Symfony `Request` so they can be reused later in the workflow by other components/services
- `LegacyController::legacyPageAction` is a generic controller responsible for executing the legacy controller, it doesn't execute `AdminController::run` but splits it into several steps
- `AdminController::checkAccess` is not called, the token in URLs is a Symfony CSRF token now and is checked by `TokenizedUrlListener`
- `LegacyController::initController`
- the permission is checked based on the detected controller and action, unless `AdminController::viewAccess` has been overridden in which case it is used to check authorization
- `SmartyVariablesFiller::fillDefault` is in charge of building most of the generic smarty variables previously handled by `AdminController::initHeader`
- `AdminController::setMedia` is in charge of defining the JS/CSS files to be loaded in the page
- `AdminController::postProcess` processes the posted data
- if after this method `AdminController::redirect_after` is defined then perform an HTTP redirection
- Check if `ajax` query parameter is defined, one of the two is executed
- `LegacyController::renderAjaxController` executes `AdminController::initContent` the appropriate `AdminController::displayAjax` method, it catches all the output data into a string buffer (because most legacy ajax methods simply echo their content) and returns it as a response
- `LegacyController::renderPageContent`
- executes `AdminController::initContent`
- fetch the Smarty `AdminController::template` to get the content of the page
- calls `Cookie::write` which was previously called via `smartyOutputContent`
- get the modal content via `AdminController::renderModal`
<div class="mermaid">
flowchart TD
A[Special Routing Matching URL] -->|Check URL with LegacyRouterChecker::check| B{Is valid?}
B -->|Yes| C[Instantiate Controller & call AdminController::init]
B -->|No| Z[Invalid URL]
C --> D[Set attributes on Symfony Request]
D --> E[LegacyController::legacyPageAction]
E --> F[LegacyController::initController]
F --> G[Check permissions based on detected controller & action]
G --> H[SmartyVariablesFiller::fillDefault]
H --> I[AdminController::setMedia]
I --> J[AdminController::postProcess]
J -->|redirect_after defined| K[Perform HTTP redirection]
J -->|redirect_after not defined| L{ajax query parameter defined?}
L -->|Yes| M[LegacyController::renderAjaxController]
L -->|No| N[LegacyController::renderPageContent]
M --> O[Execute AdminController::initContent]
O --> P[Execute AdminController::displayAjax]
P --> Q[Catch output data & return as response]
N --> R[Execute AdminController::initContent]
R --> S[Fetch AdminController::template content]
S --> T[Call Cookie::write]
T --> U[Get modal content via AdminController::renderModal]
</div>

The two workflows should render the same result. Many methods from the legacy workflow are not executed anymore because they lost their purpose, but in case you override one of those methods, be aware that they are no longer called directly, so there are breaking changes:

- `AdminController:run`
- `AdminController::initHeader`
- `AdminController::initContent`
Expand Down

0 comments on commit a08d8bb

Please sign in to comment.