Neos 8.0 "Mad Hatter" released

This Major Release of Neos and Flow 8.0 “Mad Hatter” makes bigger steps forward, raising minimum required PHP version and completely rewriting the Fusion Parser. 

– Written by


The Mad Hatter

The Mad Hatter reciting his nonsensical peom "Twinkle, Twinkle, litte Bat"
The Mad Hatter as depicted by Sir John Tenniel.

While exploring Wonderland, Alice comes across the Hatter having tea with the March Hare and the Dormouse. The Hatter explains to Alice that they are always having tea because when he tried to sing for the foul-tempered Queen of Hearts, she sentenced him to death for "murdering the time". In retaliation, Time (referred to as "he" by the Hatter) halts himself in respect to the Hatter, keeping him stuck at 6:00 pm (or 18:00) forever.


The phrase ‘mad as a hatter’ was common in Carroll’s time. ‘Mad as a hatter’ probably owes its origin to the fact that hatters actually did go mad, because the mercury they used sometimes gave them mercury poisoning.



Neos 8.0 Release Highlights

User Impersonation for Admins

Neos now allows administrators to switch into accounts of users to check if the correct access rights have been set or investigate problems with their personal workspaces or the account itself.

NodeType SVG-Preview

The node creation dialog can now display an svg preview image for the nodetypes instead of an icon. That allows to take advantage of additional space, show more details to make the distinction between different nodetypes clearer or even use colors.

Neos-8-0-PreviewIcons.png

Faster Publishing

In the past publishing sometimes took a while because many caches had to be flushed at once. With Neos 8 we optimized the caching to avoid flushing of unnecessary or duplicate tags. The support for the legacy tags from Neos 1-3 has also been removed and we added a migration that helps to adjust the last uses of those. Together with the optimizations for the simultaneous flushing of multiple tags in Flow you can expect significantly faster publishing with Neos 8.0. We measured up to 20 times faster cache-flushing which resulted in 3 times faster publishing.

New Fusion Parser

The Fusion Parser has been rewritten from scratch for this release. Now it has a modern architecture with separate lexer, parser and an intermediate object-ast. The tokens are now continuously generated - not only line by line. As a result, the parser now shows much more specific and helpful error messages and is now more forgiving for comment placements. Also, the parser aims to parse the fusion code more reliable, so many little syntax bugs were fixed and parsing is a bit stricter when it comes to really broken syntax.

The modern architecture allowed us to integrate file level caching for the intermediate parse partials. That way even in development only the actually changed fusion files are reparsed instead of the whole fusion. In the result fusion parsing in development mode is now 2-8 times faster.

A deprecated fusion feature that we removed is the support for fusion namespaces and the default namespace Neos.Fusion. All fusion prototypes now are identified by full name. A Migration for the removed default Namespace is provided but custom namespaces (which are rarely used) have to be converted manually.

Note: while the new parser changes a lot and will allow us to bring improvements to the fusion language in the next release the runtime is currently unmodified which ensures that there are no negative effects for production systems.

Neos Fusion improvements

Automatic created nested Neos.Fusion:DataStructure now support @if and @process:

attributes = Neos.Fusion:DataStructure {
  class {
    main = "bg-neos-900"
    extra = "special-class"
    extra.@if.has = ${value}
    extra.@process.wrap = ${'super-' + value}
  }
}
Array push casts the given value to array if needed. Most useful is this to add classes regardless wether a "string" or an array of strings was passed.
class = "hello"
class.@process.addClass = ${Array.push(value, "world")}

class = ${["hello", "neos"]}
class.@process.addClass = ${Array.push(value, "world")}

Fusion double-quoted strings allow now for special escapes:

string = "My \n break"

Fusion comments are allowed after any path:

comments = ${eelExpression} // Fusion rocks!

All Flow 8.0 Highlights

Support of latest PHP versions

With the release of Flow 8 we are raising the minimal PHP Version to 8.0. This allows us to take advantage of new language features especially the improved type system. With Flow 8 you can use typed properties, union types and php annotations. That way the code gets more explicit, safe and needs less commenting. 

While we decided to use PHP 8.0 as minimal PHP Version for the Flow 8.x versions we also wanted to enable using PHP 8.1 features in projects so with Flow 8 we also added support for PHP 8.1 enums.

Typed Dependency Injection

PHP 7.4 and up allows strictly typing properties, but until now Flow didn't allow using this feature to type injected properties. This was because the default lazy injection would attempt to place a different type of proxy class in place of the dependency until it is used, which makes PHP throw an error rightfully. For those cases Flow now transparently falls back to eager injection.

#[Flow\Inject]
protected LoggerInterface $logger;

If you do need lazy injection because the dependency is very heavy and only conditionally used, you still need to skip the PHP type and use a docblock annotation instead. A proper solution for this is already planned and will come with one of the upcoming releases.

/**
 * @Flow\Inject
 * @var LoggerInterface
 **/
protected $logger;

Improved Caching

The caching of Flow 8 is improved after collecting and analyzing big real world Neos projects with lots of editors. It turned out that one weakness in this regard was the flushing of many tags one by one. As a result the cache frontend and backend interfaces now allows to flush multiple tags at once (flushByTags). With that in place the flushByTags methods of the File, PDO and Redis backend were optimized for cases where multiple tags had to be flushed.

PHP Value Object Support

In Flow 8 pure php value objects are supported which are converted from simple types mapped via property mapper. To do so the ValueObject class has to provide a static fromString, fromInteger, fromFloat, fromBoolen or fromArray methods (the shortcuts fromBool and fromInt are supported as well).

For serializing such values objects inside json-array properties like the neos node properties they can implement the JsonSerializable interface.

class GeoCoordinates implements JsonSerializable
{
	/** 
     * this obvously is php 8.1, for php 8.0 getters are still needed
     */ 
    protected function __construct(
        public readonly float $latitude,
        public readonly float $longitude
    ) {
        if ($this->latitude < -90 || $latitude > 90 || $this->longitude < -180 || $longitude > 180) {
            throw new \InvalidArgumentException('invalid coordinate values');
        }
    }

    /**
     * The fromArray function is called from the property mapper to convert an
     * array to GeoCoordinates, this works for controller arguments as well as for 
     * value objects inside jsonArray properties
     */
    public static function fromArray(array $data): self
    {
        if (!isset($data['latitude'], $data['longitude'])) {
            throw new \InvalidArgumentException('latitude and longitude are required');
        }
        $latitude = (float)$data['latitude'];
        $longitude = (float)$data['longitude'];
        return new self($latitude, $longitude);
    }

    /**
     * For serializing of value objects inside of jsonArray properties the JsonSerializable
     * interface has to be implemented in addition to a matching from... method 
     */
    public function jsonSerialize(): array
    {
        return [
            'latitude' => $this->latitude,
            'longitude' => $this->longitude
        ];
    }
}

PSR 18 Http\Client 

PSR 18 specifies a http client that can be passed to external libraries for their communication needs. With Flow 8 we support PSR 18 in the Neos\Flow\Http\Browser class and ensured that it can be injected as dependency. You can also inject the Psr\Http\ClientInterfaces directly into your code.

use Neos\Flow\Annotations as Flow;

class ExampleController extends ActionController
{
        #[Flow\Inject]
        protected \Psr\Http\Client\ClientInterface $client;

		#[Flow\Inject]
        protected \Psr\Http\Message\RequestFactoryInterface $requestFactory;

        public function testAction(): string
        {
          	$request = $this->requestFactory->createRequest('get', 'https://www.flowframework.io');
			$response = $this->client->request($request);
			...
        }
}

PSR 6&16 Caching via CacheManager

Flow already supported the PSR 6 and 16 caching standards for a while but it was not easy to actually use those caches. With Flow 8.0 we added the methods getSimpleCache and get CacheItemPool to the CacheManager which allows to configure and use those caches via Caches.yaml and Objects.yaml.

MyCompany\MyPackage\SomeClass:
  properties:
    # PSR-6
    cacheItemPool:
      object:
        factoryObjectName: Neos\Flow\Cache\CacheManager
        factoryMethodName: getCacheItemPool
        arguments:
          1:
            value: MyPackage_CacheItemPool
    # PSR-16
    simpleCache:
      object:
        factoryObjectName: Neos\Flow\Cache\CacheManager
        factoryMethodName: getSimpleCache
        arguments:
          1:
            value: MyPackage_SimpleCache
namespace MyCompany\MyPackage
use Neos\Flow\Annotations as Flow;

class SomeClass {

	#[Flow\Inject]
    protected \Psr\Cache\CacheItemPoolInterface $cacheItemPool:

    #[Flow\Inject]
    protected \Psr\SimpleCache\CacheInterface $simpleCache;
}

Breaking Changes

Raised PHP and dependency requirements

The raised requirements for PHP and upgraded dependencies are most likely to require adjustments on your side. When upgrading ensure that you are running composer upgrade with the PHP version you are targeting.

Dropped support for fusion namespaces and new fusion parser

The namespace feature in fusion was deprecated and is now removed. There is a migration for the default Neos.Fusion namespace but custom namespaces have to be adjusted manually. In addition the old Fusion parser has been very generous with broken syntax for includes or missing braces. Ensure to run the fusion locally and the parser will point you the lines that need adjustment.

Dropped support for CKEditor version 4 

CKEditor 5 is default for several releases now but the neos-ui still had an option to define the default inline editor. The option was needed to be Internet Explorer compatible. As the Internet Explorer is end of life, we now also drop the possibility to choose the CKEditor version 4 as default editor. Therefore, the option "Neos.Neos.Ui.frontendConfiguration.defaultInlineEditor" has been removed. This only affects projects configured to still use the old CKEditor.

Further breaking changes

The release contains further breaking changes that are relatively unlikely to affect existing projects that are actively maintained. For the full list look at the change-logs and upgrade instructions.