Project

General

Profile

Actions

Task #82404

closed

Improve enumeration usage

Added by Romain Canon about 7 years ago. Updated over 4 years ago.

Status:
Closed
Priority:
Should have
Assignee:
Category:
-
Start date:
2017-09-09
Due date:
% Done:

100%

Estimated time:
(Total: 0.00 h)
TYPO3 Version:
8
PHP Version:
Tags:
Complexity:
Sprint Focus:

Description

TL;DR - Improve the enumeration feature to have something more similar to this implementation: https://github.com/myclabs/php-enum

Current usage

The current core enumeration implementations mainly work with string values and do not force types in any way. This can lead to incorrect values being used by functions and lead to more runtime errors.

See below some basic usage taken in current core master, and comments about what is wrong with them:

// Doubling type casting + duplicated class name
(int)(string)new VersionState(VersionState::DEFAULT_STATE)
// Duplicated class name + useless instance (could be only string comparison)
switch (VersionState::cast($row['t3ver_state'])) {
    case new VersionState(VersionState::NEW_PLACEHOLDER):
        $parts[] = 'PLH WSID#' . $row['t3ver_wsid'];
        break;
}

// A string is passed in `$conflictMode`.
//
// This should not be the responsability of the method `rename()`
// to validate and cast the value that is passed, but to the
// method that actually calls `rename()`.
//
// This can lead to more errors if not handled correctly.
public function rename($newName, $conflictMode = DuplicationBehavior::RENAME)

Goal

The main goal of the patch would be to use enumerations as value objects, which allows a much more stronger and bug-free application, as well as more flexibility in how data can be used. This also helps with onboarding new contributors who can understand the logic behind a function much more easily.


The current enumeration class (\TYPO3\CMS\Core\Type\Enumeration) would be improved.

Magic method calls

One of the new introduced features would be magic static methods calls, to dramatically ease readability:

// Old way:
$myEnum = MyEnum::cast(MyEnum::MY_VALUE);

// New way:
$myEnum = MyEnum::MY_VALUE();

Data mapping

The data mapper automatically fills enum values, which means a frontend form being submitted can then create a PHP object containing enum values, which is awesome when you actually have to work with the object.

Utility methods

Enumeration classes should use more utility functions to ease the usage when working with implementation.

TYPO3 core already uses this kind of methods (see TYPO3\CMS\Backend\Toolbar\Enumeration\InformationStatus::isGreaterThan()), but there could be much more.

Example

See below an implementation example of how TYPO3 page types could be handled:

// The actual enumeration class.
class PageType extends Enumeration
{
    // Pages
    const STANDARD = 1;
    const BACKEND_USER_SECTION = 6;

    // Links
    const SHORTCUT = 4;
    const MOUNT_POINT = 7;
    const EXTERNAL_URL = 3;

    // Special
    const FOLDER = 254;
    const RECYCLER = 255;
    const MENU_SEPARATOR = 199;

    /**
     * Will be true if the type is one of the links types.
     *
     * @return bool
     */
    public function isLink(): bool
    {
        return $this->equal(static::SHORTCUT())
            || $this->equal(static::MOUNT_POINT())
            || $this->equal(static::EXTERNAL_URL());
    }

    /**
     * Will be true only if the type is folder, false in any other case.
     *
     * @return bool
     */
    public function isFolder(): bool
    {
        return $this->equals(static::FOLDER());
    }
}

// A class using the enumeration.
class Page 
{
    /**
     * @var PageType
     */
    protected $type;

    /**
     * @param PageType $type
     */
    public function setType(PageType $type)
    {
        $this->type = $type;
    }

    /**
     * @return PageType
     */
    public function getType()
    {
        return $this->type;
    }
}

// Basic usage.
class MyClass
{
    public function process()
    {
        $page = new Page;
        $page->setType(PageType::STANDARD());

        // ...

        $this->doSomething($page);        
    }

    /**
     * @param Page $page
     */
    protected function doSomething(Page $page)
    {
        if ($page->getType()->isFolder()) {
            // Do folder related things...
        } elseif ($page->getType()->isLink()) {
            // Create some link...
        } elseif ($page->getType()->equals(PageType::STANDARD())) {
            // ...
        } else {
            // ...
        }
    }
}

Please note that this whole implementation has already been done and used in my company for a whole year now, using this tiny library: https://github.com/myclabs/php-enum

Every developer uses it almost every day and everyone did say this improved productivity, code understanding and application reliability by a lot, compared to an old string-comparison way.


Subtasks 3 (0 open3 closed)

Task #82411: Breaking: refactor enumeration classClosedRomain Canon2017-09-09

Actions
Feature #82412: Add magic static call to enumeration classClosedRomain Canon2017-09-09

Actions
Task #82413: Change enumeration usage in coreClosedRomain Canon2017-09-09

Actions
Actions

Also available in: Atom PDF