> ## Documentation Index
> Fetch the complete documentation index at: https://developer.upsun.com/llms.txt
> Use this file to discover all available pages before exploring further.

# PHP 8.0 brings it all together on Platform.sh

> Discover the new features in PHP 8.0 and learn how to upgrade your Platform.sh projects. Constructor Property Promotion, Attributes, Named Arguments, and more!

export const PostMeta = ({data = {}}) => {
  const {author, date} = data;
  const authors = Array.isArray(author) ? author : author ? [author] : [];
  const resolveAuthor = slug => {
    const entry = AUTHOR_MAP[slug] || ({});
    const name = entry.name || slug;
    const github = entry.github || null;
    const linkedin = entry.linkedin || null;
    const url = github ? `https://github.com/${github}` : linkedin || null;
    const avatarUrl = github ? `https://github.com/${github}.png?size=64` : null;
    return {
      name,
      url,
      avatarUrl
    };
  };
  const formattedDate = date ? new Date(date).toLocaleDateString('en-US', {
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  }) : null;
  if (authors.length === 0 && !formattedDate) return null;
  const AUTHOR_MAP = {
    "aaron-collier": {
      "name": "Aaron Collier"
    },
    "aaron-dudenhofer": {
      "name": "Aaron Dudenhofer"
    },
    "aaron-porter": {
      "name": "Aaron Porter"
    },
    "adriaan-odendaal": {
      "name": "Adriaan Odendaal"
    },
    "ajmal": {
      "name": "Ajmal Siddiqui"
    },
    "akalipetis": {
      "name": "Antonis Kalipetis"
    },
    "alexander-varwijk": {
      "name": "Alexander Varwijk"
    },
    "alicia-bevilacqua": {
      "name": "Alicia Bevilacqua"
    },
    "amelie-deguerry": {
      "name": "Amelie Deguerry"
    },
    "anacidre": {
      "name": "Ana Cidre",
      "linkedin": "https://www.linkedin.com/in/ana-cidre"
    },
    "andoni": {
      "name": "Andoni Auzmendi"
    },
    "andrei-taranu": {
      "name": "Andrei (Alex) Taranu",
      "linkedin": "https://www.linkedin.com/in/andrei-alex-taranu/"
    },
    "andrew-baxter": {
      "name": "Andrew Baxter"
    },
    "andrew-melck": {
      "name": "Andrew Melck"
    },
    "antoine-crochet-damais": {
      "name": "Antoine Crochet Damais"
    },
    "augustin-delaporte": {
      "name": "Augustin Delaporte",
      "linkedin": "https://www.linkedin.com/in/augustindelaporte/"
    },
    "branislav-bujisic": {
      "name": "Branislav Bujisic"
    },
    "carl-smith": {
      "name": "Carl Smith"
    },
    "caroline-leroy": {
      "name": "Caroline Leroy"
    },
    "cati-mayer": {
      "name": "Cati Mayer"
    },
    "catplat": {
      "name": "C Trinkwon"
    },
    "ceelolulu": {
      "name": "Celeste van der Watt"
    },
    "chadwcarlson": {
      "name": "Chad Carlson",
      "github": "chadwcarlson",
      "linkedin": "https://www.linkedin.com/in/chadwcarlson"
    },
    "chris-ward": {
      "name": "Chris Ward"
    },
    "chris-yates": {
      "name": "Chris Yates"
    },
    "christian-sieber": {
      "name": "Christian Sieber"
    },
    "christopher-lockheardt": {
      "name": "Christopher Lockheardt"
    },
    "christopher-skene": {
      "name": "Christopher Skene"
    },
    "chuck-morgan": {
      "name": "Chuck Morgan"
    },
    "corey-dockendorf": {
      "name": "Corey Dockendorf"
    },
    "crell": {
      "name": "Crell"
    },
    "damz": {
      "name": "Damz"
    },
    "dan-morrison": {
      "name": "Dan Morrison"
    },
    "davidbonachera": {
      "name": "David Bonachera",
      "github": "davidbonachera",
      "linkedin": "https://www.linkedin.com/in/davidbonachera"
    },
    "dereliahmet1": {
      "name": "Ahmet Faruk Dereli"
    },
    "devicezero": {
      "name": "Jonas Kröger",
      "github": "devicezero",
      "linkedin": "https://www.linkedin.com/in/jonaskroeger/"
    },
    "doug-goldberg": {
      "name": "Doug Goldberg"
    },
    "duncan-naves": {
      "name": "Duncan Naves",
      "github": "duncannaves",
      "linkedin": "https://www.linkedin.com/in/duncan-naves-a94423aa"
    },
    "erika-bustamante": {
      "name": "Erika Bustamante"
    },
    "fabpot": {
      "name": "Fabien Potencier"
    },
    "flovntp": {
      "name": "Florent Huck",
      "github": "flovntp",
      "linkedin": "https://www.linkedin.com/in/florenthuck"
    },
    "fred-plais": {
      "name": "Fred Plais"
    },
    "gauthier-garnier": {
      "name": "Gauthier Garnier"
    },
    "gilzow": {
      "name": "Paul Gilzow"
    },
    "gmoigneu": {
      "name": "Guillaume Moigneu",
      "github": "gmoigneu",
      "linkedin": "https://www.linkedin.com/in/guillaumemoigneu/"
    },
    "gregqualls": {
      "name": "Greg Qualls"
    },
    "guguss": {
      "name": "Augustin Delaporte"
    },
    "haylee-millar": {
      "name": "Haylee Millar"
    },
    "ivana-kotur": {
      "name": "Ivana Kotur"
    },
    "jackrabbithanna": {
      "name": "Mark Hanna"
    },
    "jared-wright": {
      "name": "Jared Wright",
      "github": "jww-sh",
      "linkedin": "https://www.linkedin.com/in/jaredwaynewright"
    },
    "jessica-orozco": {
      "name": "Jessica Orozco"
    },
    "joey-stanford": {
      "name": "Joey Stanford"
    },
    "john-grubb": {
      "name": "John Grubb"
    },
    "jonas-kruger": {
      "name": "Jonas Kruger"
    },
    "kathryn-frazer": {
      "name": "Kathryn Frazer"
    },
    "kemiojo": {
      "name": "Kemi Elizabeth Ojogbede"
    },
    "kieronsambrook-smith": {
      "name": "Kieronsambrook Smith"
    },
    "laurent-arnoud": {
      "name": "Laurent Arnoud",
      "linkedin": "https://www.linkedin.com/in/laurent-arnoud-861b44121/"
    },
    "letoya-boyne": {
      "name": "Letoya Boyne"
    },
    "lolautruche": {
      "name": "Jérôme Vieilledent"
    },
    "lyly-lepinay": {
      "name": "Lyly Lepinay"
    },
    "manauwar-alam": {
      "name": "Manauwar Alam"
    },
    "marc-antoine-porri": {
      "name": "Marc Antoine Porri"
    },
    "maria-antinkaapo": {
      "name": "Maria Antinkaapo"
    },
    "maria-de-anton": {
      "name": "Maria De Anton"
    },
    "mark-dorison": {
      "name": "Mark Dorison"
    },
    "markus-hausammann": {
      "name": "Markus Hausammann"
    },
    "mary-thomas": {
      "name": "Mary Thomas"
    },
    "mathias-bolt-lesniak": {
      "name": "Mathias Bolt Lesniak"
    },
    "mathieu-strauch": {
      "name": "Mathieu Strauch"
    },
    "matthias-van-woensel": {
      "name": "Matthias Van Woensel",
      "linkedin": "https://www.linkedin.com/in/matthias-van-woensel-267a069"
    },
    "maz-mohammadi": {
      "name": "Maz Mohammadi"
    },
    "michael-sharp": {
      "name": "Michael Sharp"
    },
    "mupsi": {
      "name": "Marine Gandy"
    },
    "natalie-harper": {
      "name": "Natalie Harper"
    },
    "ngommenginger": {
      "name": "Nicolas Gommenginger",
      "linkedin": "https://www.linkedin.com/in/nicolas-gommenginger"
    },
    "nicholas-bennison": {
      "name": "Nicholas Bennison"
    },
    "nicholas-vahalik": {
      "name": "Nicholas Vahalik"
    },
    "nick-hardiman": {
      "name": "Nick Hardiman"
    },
    "nickanderegg": {
      "name": "Nickanderegg"
    },
    "nicolas-grekas": {
      "name": "Nicolas Grekas",
      "github": "nicolas-grekas",
      "linkedin": "https://www.linkedin.com/in/nicolasgrekas/"
    },
    "niti-malwade": {
      "name": "Niti Malwade"
    },
    "opensocialteam": {
      "name": "Opensocialteam"
    },
    "ori-pekelman": {
      "name": "Ori Pekelman"
    },
    "otavio-santana": {
      "name": "Otavio Santana"
    },
    "palwandi": {
      "name": "Pawan Alwandi",
      "github": "pawpy",
      "linkedin": "https://www.linkedin.com/in/pawanalwandi"
    },
    "patrick-boest": {
      "name": "Patrick Boest"
    },
    "patrick-dawkins": {
      "name": "Patrick Dawkins",
      "github": "pjcdawkins",
      "linkedin": "https://www.linkedin.com/in/patrickdawkins"
    },
    "patrick-klima": {
      "name": "Patrick Klima"
    },
    "pjcdawkins": {
      "name": "Pjcdawkins"
    },
    "prineet-kaurbhurji": {
      "name": "Prineet Kaurbhurji"
    },
    "quentin-sinig": {
      "name": "Quentin Sinig"
    },
    "ralt": {
      "name": "Florian Margaine",
      "github": "ralt",
      "linkedin": "https://www.linkedin.com/in/florian-margaine-43971136"
    },
    "ramanathanramakrishnamurthy": {
      "name": "Ramanathanramakrishnamurthy"
    },
    "remi-lejeune": {
      "name": "Rémi Lejeune"
    },
    "ribel": {
      "name": "Taras Kruts"
    },
    "robert-douglass": {
      "name": "Robert Douglass"
    },
    "rudy-weber": {
      "name": "Rudy Weber"
    },
    "ryan-hicks": {
      "name": "Ryan Hicks"
    },
    "sabri-helal": {
      "name": "Sabri Helal"
    },
    "savannah-bergeron": {
      "name": "Savannah Bergeron"
    },
    "shannon-vettes": {
      "name": "Shannon Vettes"
    },
    "shawn-ogasawara": {
      "name": "Shawn Ogasawara",
      "linkedin": "https://www.linkedin.com/in/shawn-ogasawara-83a9a0/"
    },
    "shawna-spoor": {
      "name": "Shawna Spoor"
    },
    "shedrack-akintayo": {
      "name": "Shedrack Akintayo"
    },
    "simon-ruggier": {
      "name": "Simon Ruggier"
    },
    "sophie-van-der-kindere": {
      "name": "Sophie Van Der Kindere"
    },
    "stefanos-thampis": {
      "name": "Stefanos Thampis"
    },
    "stephen-weinberg": {
      "name": "Stephen Weinberg"
    },
    "sukhman-virk": {
      "name": "Sukhman Virk"
    },
    "sumaira-nazir": {
      "name": "Sumaira Nazir"
    },
    "sumer": {
      "name": "Sümer Cip"
    },
    "syed-raza": {
      "name": "Syed Raza"
    },
    "tamara-bacchia": {
      "name": "Tamara Bacchia"
    },
    "tara-arnold": {
      "name": "Tara Arnold"
    },
    "theosakamg": {
      "name": "Mickael Gaillard",
      "github": "theosakamg"
    },
    "thomasdiluccio": {
      "name": "Thomas di Luccio"
    },
    "tim-anderson": {
      "name": "Tim Anderson"
    },
    "tom-helmer-hansen": {
      "name": "Tom Helmer Hansen"
    },
    "tylermills": {
      "name": "Tyler Mills"
    },
    "upsun": {
      "name": "Upsun"
    },
    "veronika-tolkachova": {
      "name": "Veronika Tolkachova",
      "linkedin": "https://www.linkedin.com/in/veronika-tolkachova-169167a2"
    },
    "vince-parker": {
      "name": "Vince Parker"
    },
    "vinnie-russo": {
      "name": "Vincenzo Russo"
    },
    "vrobert78": {
      "name": "Vincent Robert",
      "github": "vrobert78",
      "linkedin": "https://www.linkedin.com/in/vincent-robert-498a883"
    },
    "yuriy-babenko": {
      "name": "Yuriy Babenko"
    },
    "yuriy-gerasimov": {
      "name": "Yuriy Gerasimov"
    }
  };
  return <div className="post-meta">
      {(authors.length > 0 || formattedDate) && <div className="post-meta-info">
          {authors.length > 0 && <div className="post-meta-authors">
              {authors.map(slug => {
    const {name, url, avatarUrl} = resolveAuthor(slug);
    const inner = <>
                    {avatarUrl && <img src={avatarUrl} alt={name} className="post-meta-avatar" />}
                    <span className="post-meta-author-name">{name}</span>
                  </>;
    return url ? <a key={slug} href={url} target="_blank" rel="noopener noreferrer" className="post-meta-author">
                    {inner}
                  </a> : <span key={slug} className="post-meta-author">{inner}</span>;
  })}
            </div>}
          {authors.length > 0 && formattedDate && <span className="post-meta-separator" aria-hidden="true">·</span>}
          {formattedDate && <span className="post-meta-date">{formattedDate}</span>}
        </div>}
    </div>;
};

<PostMeta data={{ author: ["crell"], date: "2020-11-26", image: "/images/posts/unknown/php-80-brings-it-all-together-on-platformsh/php-80-brings-it-all-together-on-platformsh.webp" }} />

<Tip>
  This post was originally published on the Platform.sh blog and reflects information from the time of publication.
</Tip>

A year in the making, PHP 8.0 has been released to the world! Sporting a plethora of new functionality to offer more power with less code, it's the most anticipated release of PHP since ... well since [PHP 7.4 last year](/posts/unknown/php-7.4-is-just-a-git-push-away). (PHP is exciting, what can I say?) And you can run it today on Platform.sh.

## Stronger together

As we've been covering throughout our series, PHP 8.0 is chock full of new functionality. From a [more expressive typing system](https://upsun.com/blog/php-8-0-type-improvements/) to [`match()` expressions](https://upsun.com/blog/php-8-0-type-improvements/) to the potentially huge improvement in persistent services from the new [JIT extension](https://upsun.com/blog/php-8-0-type-improvements/), PHP 8 has something to offer everyone.

There are three features I want to call out in particular, though, as the most important. These features are all long-standing requests on their own, but in tandem with each other create something greater than the sum of their parts.

## Constructor Property Promotion

The first is [constructor property promotion](/posts/releases/php-80-feature-focus-constructor-property-promotion). Constructor promotion is "just" syntactic sugar to make writing constructors and object properties less repetitive. For example, this class:

```php theme={null}
<?php

class MailMessage
{
    private string $to;
    private string $subject;
    private string $from;
    private string $body;
    private array $cc;
    private array $bcc;
    private string $attachmentPath;

    public function __construct(
        string $to;
        string $subject;
        string $from;
        string $body;
        array $cc = [];
        array $bcc = [];
        string ?$attachmentPath = null;
    ) {
        $this->to = $to;
        $this->subject = $subject;
        $this->from = $from;
        $this->body = $body;
        $this->cc = $cc;
        $this->bcc = $bcc;
        $this->attachmentPath = $attachmentPath;
    }
}
```

Can now be condensed into this:

```php theme={null}
<?php

Class MailMessage
{
    public function __construct(
        private string $to,
        private string $subject,
        private string $from,
        private string $body,
        private array $cc = [],
        private array $bcc = [],
        private ?string $attachmentPath = null,
    ) {}
}
```

Exact same result, one quarter the typing. And of course you can still use non-promoted properties and parameters, still fill in a constructor body for additional behavior, etc.

While that may seem like a small change, it's actually quite huge. Not just because it reduces needless verbosity (which is great), but because it makes certain approaches easier. In particular, it makes defining a struct-like object (one that contains only public properties mapped from the constructor, or little more than that) 4x easier to write.

Today, many developers will default to using associative arrays for quick-n-dirty structs. That is less self-documenting, [uses more memory](https://peakd.com/php/@crell/php-use-associative-arrays-basically-never), is harder to use, and is all around worse in every way than defining a proper class—except a lot of developers find creating a class for that purpose too much work. Well, now defining a class is not too much work and can vastly improve the quality and efficiency of a code base.

## Attributes

The second cornerstone feature is attributes. Attributes provide a way to annotate a class, function, property, or argument with metadata that can be examined at runtime, with full native language support. Such functionality was available before via third-party libraries and custom parsing of comment blocks, but now it's available as a first-class part of the language.

Attributes can be either bare strings or map to classes, with the latter usually being preferable (for all the same reasons why classes are preferable to unstructured arrays above). Although attributes don't intrinsically have any particular functionality, that is easy enough to add in user-space. For example, to require that the subject line of an email message contain only plain text and be limited to 120 characters, you could define an attribute:

```php theme={null}
<?php

#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER)]
class PlainText
{
    public function __construct(private ?int $length = null) {}

    public function valid(string $str): bool
    {
        return (strlen($str) <= $this->length) && strip_tags($str) === $str;
    }
}
```

Note the constructor promotion at work. Attributes are a perfect example of struct classes, and so constructor promotion is a natural fit. We also flag the attribute as only being valid on properties and parameters, as it wouldn't make any logical sense on a class or function.

Now the subject line property can be tagged:

```php theme={null}
<?php

Class MailMessage
{
    public function __construct(
        private string $to,
        #[PlainText(120)]
        private string $subject,
        private string $from,
        private string $body,
        private array $cc = [],
        private array $bcc = [],
        private ?string $attachmentPath = null,
    ) {}
}
```

And then at any time we can validate a `MailMessage` object:

```php theme={null}
<?php

$attribs = new \ReflectionObject($addr)
    ->getProperty('subject')
    ->getAttributes(PlainText::class);
if (! $attribs[0]->valid($mail->subject)) {
  throw new SomethingIsVeryWrongException();
}
```

(In practice you'd want something a bit more robust, but let's keep it simple for now.)

All of that is with native PHP code; no third-party libraries needed. That means the attribute syntax is also available to linters for your IDE to color-code, type check, syntax lint, autocomplete, etc.

And that's just the tip of the iceberg of what meta-programming is now available natively in PHP.

## Named arguments

The third and final part of the PHP 8.0 trifecta is named arguments. Named arguments are what they say on the tin. Rather than calling a function with positional arguments, any function or method may now be called with named parameters, in arbitrary order:

```php theme={null}
<?php

$found = in_array(needle: 'a', haystack: $arr);
```

While named parameters can be used in almost any situation to improve readability (especially where the parameters are not immediately obvious from context), one of their most important use cases is ... calling constructors.

```php theme={null}
<?php

$mail = new MailMessage(
    subject: 'Write more blog posts about PHP!',
    body: 'PHP is awesome and we should have more articles about it',
    to: 'Larry Garfield',
    from: 'Robert Douglas',
    attachmentPath: '/path/to/some/file',
);
```

Note in particular that the arguments are not in their declared order, and that's fine. We're also skipping some optional arguments entirely as we don't need them.

## Bringing it all together

Constructor property promotion, attributes, and named arguments are all valuable additions in their own right. The combination of them, however, is where PHP 8.0 really shines and where it's going to change how we write PHP code for years to come.

As an example, let's look at real code from my [PSR-14 Event Dispatcher](https://www.php-fig.org/psr/psr-14/) implementation, [Tukio](https://github.com/Crell/Tukio). It defines its own attributes, like so:

```php theme={null}
<?php

namespace Crell\Tukio;

use \Attribute;

#[Attribute(Attribute::TARGET_FUNCTION | Attribute::TARGET_METHOD)]
class Listener implements ListenerAttribute
{
   public function __construct(
       public ?string $id = null,
       public ?string $type = null,
   ) {}
}

#[Attribute(Attribute::TARGET_FUNCTION | Attribute::TARGET_METHOD)]
class ListenerBefore implements ListenerAttribute
{
   public function __construct(
       public string $before,
       public ?string $id = null,
       public ?string $type = null,
   ) {}
}

#[Attribute(Attribute::TARGET_FUNCTION | Attribute::TARGET_METHOD)]
class ListenerPriority implements ListenerAttribute
{
   public function __construct(
       public ?int $priority,
       public ?string $id = null,
       public ?string $type = null,
   ) {}
}
```

(And one or two others I'm skipping for space.)

All of them are trivially defined using constructor promotion. Those attributes can then decorate listeners, either functions or methods, in order to define how they get registered. In most cases to specify a custom ID you can do:

```php theme={null}
<?php

#[Listener('mine')]
function my_listener(MyEvent $event): void
{
    // ...
}
```

However, especially when using subscriber classes (a class that contains a bunch of listeners), you can mix and match any of them. And many of them are easier to use as named arguments:

```php theme={null}
<?php

class AttributedSubscriber
{
   #[Listener('a')]
   public function onA(CollectingEvent $event) : void { ... }

   #[ListenerPriority(priority: 5, id: 'this_listener')]
   public function onB(CollectingEvent $event) : void { ... }

   #[ListenerBefore(id: 'listener_c', before: 'a')]
   public function onC(CollectingEvent $event) : void { ... }

   #[ListenerAfter(after: 'a', id: 'extra_listener')]
   public function onD(CollectingEvent $event) : void { ... }

   #[Listener]
   public function onG(RareEvent $event) : void { ... }
}
```

The entire API is driven by attributes, using named arguments, which themselves were defined with constructor promotion: Highly readable, compact code with a major functionality boost.

That's the power of designing functionality to play well together: They end up playing well together.

## Try it out today

Of course, PHP 8 does have [some changes](/posts/unknown/php-80-feature-focus-language-tightening) that may trip up older code. As with any PHP version, you want to test your code before you just update. And testing PHP 8.0 on Platform.sh couldn't be easier.

First, make a new branch in your repository. Then update your `.platform.app.yaml` file in that branch and change the `type` key to:

```yaml theme={null}
type: php:8.0
```

Git commit and deploy. That branch is now running on PHP 8.0. Try it out, run your integration tests, spot check it, and otherwise see if anything needs to be updated.

Once you've made whatever changes are needed, merge that branch back to `master`. Congratulations, you're now running PHP 8.0 on production.

Our PHP 8.0 containers also have two other new features. For one, they bundle the new [Composer 2.0](https://blog.packagist.com/composer-2-0-is-now-available/) by default. Composer 2.0 offers considerable performance improvements locating and downloading dependencies, but does require some Composer extensions to be updated to support it. Make sure your project is updated accordingly. We've also updated the version of Node.js that ships in the PHP 8.0 container to the latest LTS version, Node 12.

Even if you're not ready to update to PHP 8.0 today, you should still make a plan for it. Security support for PHP 7.2 expires 30 November, and 7.3 goes into security-only mode for another year. If you're still on older PHP versions, not only are you missing out on the performance improvements and new functionality, but you may be running insecure PHP versions, too. Make version upgrades a regular part of your maintenance cycle and it won't really be a burden at all, but a pleasure to look forward to. After each upgrade, you get all the new fun toys to play with.

## Not quite the end of the journey

Thank you for taking this journey with us through the new features of PHP 8.0. It's a major release of a major language, and this has been our longest and most detailed blog series to date.

It's not quite over, however. PHP 8.1 is already in development, and some of the features that are already in progress ... for now let's just say that, if they actually happen, 8.1 may earn an even bigger party than 8.0. Stay tuned.

And there's a few smaller features of PHP 8.0 that didn't quite make it into this series, so we’re turning this blog series into an ebook and fleshing it out with the stuff that didn't make the cut. Expect that to be released here on Platform.sh before the end of the year.

Happy Deploying, PHP!
