{"id":15030644,"url":"https://github.com/smknstd/modern-php-cheatsheet","last_synced_at":"2025-05-14T16:15:25.681Z","repository":{"id":41559851,"uuid":"414704410","full_name":"smknstd/modern-php-cheatsheet","owner":"smknstd","description":"Cheatsheet for some PHP knowledge you will frequently encounter in modern projects.","archived":false,"fork":false,"pushed_at":"2025-02-11T15:20:15.000Z","size":133,"stargazers_count":1297,"open_issues_count":1,"forks_count":110,"subscribers_count":43,"default_branch":"main","last_synced_at":"2025-04-14T18:02:40.054Z","etag":null,"topics":["cheatsheet","php8-features"],"latest_commit_sha":null,"homepage":"https://smknstd.github.io/modern-php-cheatsheet/","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/smknstd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-10-07T17:52:56.000Z","updated_at":"2025-04-11T08:38:17.000Z","dependencies_parsed_at":"2025-03-24T13:44:01.430Z","dependency_job_id":"a723aa80-acd1-44fb-a8e7-01176af58981","html_url":"https://github.com/smknstd/modern-php-cheatsheet","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smknstd%2Fmodern-php-cheatsheet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smknstd%2Fmodern-php-cheatsheet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smknstd%2Fmodern-php-cheatsheet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smknstd%2Fmodern-php-cheatsheet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/smknstd","download_url":"https://codeload.github.com/smknstd/modern-php-cheatsheet/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248933339,"owners_count":21185460,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cheatsheet","php8-features"],"created_at":"2024-09-24T20:13:56.730Z","updated_at":"2025-04-14T18:04:41.034Z","avatar_url":"https://github.com/smknstd.png","language":null,"funding_links":[],"categories":["Tutorials \u0026 Blogs","Resources"],"sub_categories":[],"readme":"# Modern PHP Cheatsheet\n\n![Modern PHP Cheatsheet](https://i.imgur.com/2STEtgG.png)\n\n\n\u003e If you like this content, you can ping me or follow me on Twitter :+1:\n\n[![Tweet for help](https://img.shields.io/twitter/follow/smknstd?label=Tweet%20%40smknstd\u0026style=social)](https://twitter.com/smknstd/)\n\n## Introduction\n\n### Motivation\n\nThis document is a cheatsheet for PHP you will frequently encounter in modern projects and most contemporary sample code.\n\nThis guide is not intended to teach you PHP from the ground up, but to help developers with basic knowledge who may struggle to get familiar with modern codebases (or let's say to learn Laravel or Symfony for instance) because of the new PHP concepts and features introduced over the years.\n\n\u003e **Note:** Concepts introduced here are based on the most recent version of PHP available ([PHP 8.3](https://www.php.net/releases/8.3/en.php) at the time of the last update)\n\n### Complementary Resources\n\nWhen you struggle to understand a notion, I suggest you look for answers on the following resources:\n- [Stitcher's blog](https://stitcher.io/blog)\n- [PHP.Watch](https://php.watch/versions)\n- [Exploring PHP 8.0](https://leanpub.com/exploringphp80)\n- [PHP 8 in a nutshell](https://amitmerchant.gumroad.com/l/php8-in-a-nutshell)\n- [PHP The Right Way](https://phptherightway.com/)\n- [StackOverflow](https://stackoverflow.com/questions/tagged/php)\n\n### Recent PHP releases\n\n| Version                                            | Release date  |\n|----------------------------------------------------|---------------|\n| [PHP 8.4](https://www.php.net/releases/8.4/en.php) | November 2024 |\n| [PHP 8.3](https://www.php.net/releases/8.3/en.php) | December 2023 |\n| [PHP 8.2](https://www.php.net/releases/8.2/en.php) | December 2022 |\n| [PHP 8.1](https://www.php.net/releases/8.1/en.php) | November 2021 |\n| [PHP 8.0](https://www.php.net/releases/8.0/en.php) | November 2020 |\n| PHP 7.4                                            | November 2019 |\n| PHP 7.3                                            | December 2018 |\n| PHP 7.2                                            | November 2017 |\n| PHP 7.1                                            | December 2016 |\n| PHP 7.0                                            | December 2015 |\n\nMore infos on [php.net](https://www.php.net/supported-versions.php).\n\n## Table of Contents\n\n- [Modern PHP cheatsheet](#modern-php-cheatsheet)\n    * [Introduction](#introduction)\n        + [Motivation](#motivation)\n        + [Complementary resources](#complementary-resources)\n        + [Recent PHP releases](#recent-php-releases)\n    * [Table of contents](#table-of-contents)\n    * [Notions](#notions)\n        + [Function default parameter value](#function-default-parameter-value)\n        + [Trailing comma](#trailing-comma)\n        + [Type declaration](#type-declaration)\n        + [Destructuring arrays](#destructuring-arrays)\n        + [Null Coalescing](#null-coalescing)\n        + [Nullsafe operator](#nullsafe-operator)\n        + [Spread operator](#spread-operator)\n        + [Named arguments](#named-arguments)\n        + [Short closures](#short-closures)\n        + [Match expression](#match-expression)\n        + [Stringable interface](#stringable-interface)\n        + [Enums](#enums)\n        + [Multiple lines string syntax](#multiple-lines-string-syntax)\n\n## Notions\n\n### Function default parameter value\n\nYou can set default value to your function parameters:\n\n```php\nfunction myFunction($param = 'foo')\n{\n    return $param;\n}\n$a = myFunction();\n// $a = 'foo'\n\n$b = myFunction('bar');\n// $b = 'bar'\n```\n\nBut if you send null or an undefined property, default value won't be used:\n\n```php\nfunction myFunction($param = 'foo')\n{\n    return $param;\n}\n$a = myFunction(null);\n// $a = null\n\n$b = myFunction($undefined); // PHP Warning:  Undefined variable $undefined\n// $b = null\n```\n\n### Trailing comma\n\nA trailing comma, also known as a dangling comma, is a comma symbol that is typed after the last item of a list of elements. One of the major benefits when used with multilines, is that [diff outputs are cleaner](https://medium.com/@nikgraf/why-you-should-enforce-dangling-commas-for-multiline-statements-d034c98e36f8).\n\n#### Array\n\nYou can use trailing comma in arrays :\n\n```php\n$array = [\n    'foo',\n    'bar',\n];\n```\n\n#### Grouped use statement\n\n![php-version-72](https://shields.io/badge/php-\u003e=7.2-blue)\n\nSince PHP 7.2, you can use trailing comma in grouped use statement:\n\n```php\nuse Symfony\\Component\\HttpKernel\\{\n    Controller\\ControllerResolverInterface,\n    Exception\\NotFoundHttpException,\n    Event\\PostResponseEvent,\n};\n```\n\n#### Function and method call\n\n![php-version-73](https://shields.io/badge/php-\u003e=7.3-blue)\n\nSince PHP 7.3, you can use trailing comma when calling a function:\n\n```php\nfunction myFunction($foo, $bar)\n{\n    return true;\n}\n$a = myFunction(\n    'baz',\n    'qux',\n);\n```\n\nand when calling a method:\n\n```php\n$f = new Foo();\n$f-\u003emyMethod(\n    'baz',\n    'qux',\n);\n```\n\n#### Function parameters\n\n![php-version-80](https://shields.io/badge/php-\u003e=8.0-blue)\n\nSince PHP 8.0, you can use trailing comma when declaring function parameters:\n\n```php\nfunction myFunction(\n    $foo,\n    $bar,\n)\n{\n    return true;\n}\n```\n\n#### Closure's use statement\n\n![php-version-80](https://shields.io/badge/php-\u003e=8.0-blue)\n\nSince PHP 8.0, you can use trailing comma with closure's use statement:\n\n```php\nfunction() use (\n    $foo,\n    $bar,\n)\n{\n    return true;\n}\n```\n\n### Type declaration\n\n![php-version-70](https://shields.io/badge/php-\u003e=7.0-blue)\n\nWith Type declaration you can specify the expected data type for a property that will be enforce at runtime. It supports many types like scalar types (int, string, bool, and float) but also array, iterable, object, stdClass, etc.\n\nYou can set a type to a function's parameter:\n\n```php\nfunction myFunction(int $param)\n{\n    return $param;\n}\n$a = myFunction(10);\n// $a = 10\n$b = myFunction('foo'); // TypeError: myFunction(): Argument #1 ($param) must be of type int, string given\n```\n\nYou can set a return type to a function:\n\n```php\nfunction myFunction(): int\n{\n    return 'foo';\n}\n$a = myFunction(); // TypeError: myFunction(): Return value must be of type int, string returned\n```\n\nWhen a function should not return something, you can use the type \"void\":\n\n```php\nfunction myFunction(): void\n{\n    return 'foo';\n}\n// PHP Fatal error:  A void function must not return a value\n```\n\nYou cannot return null either:\n\n```php\nfunction myFunction(): void\n{\n    return null;\n}\n// PHP Fatal error:  A void function must not return a value\n```\n\nHowever, using return to exit the function is valid:\n\n```php\nfunction myFunction(): void\n{\n    return;\n}\n$a = myFunction();\n// $a = null\n```\n\n#### Class property\n\n![php-version-74](https://shields.io/badge/php-\u003e=7.4-blue)\n\nYou can set a type to a class property:\n\n```php\nClass Foo\n{\n    public int $bar;\n}\n$f = new Foo();\n$f-\u003ebar = 'baz'; // TypeError: Cannot assign string to property Foo::$bar of type int\n```\n\n#### Union type\n\n![php-version-80](https://shields.io/badge/php-\u003e=8.0-blue)\n\nSince PHP 8.0, you can use a “union type” that accepts values of multiple different types, rather than a single one:\n\n```php\nfunction myFunction(string|int|array $param): string|int|array\n{\n    return $param;\n}\n```\n\nIt also works with class property:\n\n```php\nClass Foo\n{\n    public string|int|array $bar;\n}\n```\n\n#### Intersection type\n\n![php-version-81](https://shields.io/badge/php-\u003e=8.1-blue)\n\nSince PHP 8.1, you can use an \"intersection type\" (also known as \"pure\") that enforce that a given value belong to every types. For example this param needs to implement both *Stringable* and *Countable* interfaces:\n\n```php\nfunction myFunction(Stringable\u0026Countable $param): Stringable\u0026Countable\n{\n    return $param;\n}\nClass Foo\n{\n    public function __toString() {\n        return \"something\";\n    }\n}\nmyFunction(new Foo());\n// TypeError: myFunction(): Argument #1 ($param) must be of type Stringable\u0026Countable, Foo given\n```\n\nIt also works with class property:\n\n```php\nClass Foo\n{\n    public Stringable\u0026Countable $bar;\n}\n```\n\nIntersection type only supports class and interfaces. Scalar types (string, int, array, null, mixed, etc) are not allowed:\n\n```php\nfunction myFunction(string\u0026Countable $param)\n{\n    return $param;\n}\n// PHP Fatal error:  Type string cannot be part of an intersection type\n```\n\n##### External resource\n\n- [Intersection types on PHP.Watch](https://php.watch/versions/8.1/intersection-types)\n\n#### Nullable type\n\n![php-version-71](https://shields.io/badge/php-\u003e=7.1-blue)\n\nWhen a parameter has no type, it can accept null value:\n\n```php\nfunction myFunction($param)\n{\n    return $param;\n}\n$a = myFunction(null);\n// $a = null\n```\n\nBut as soon as a parameter has a type, it won't accept null value anymore and you'll get an error:\n\n```php\nfunction myFunction(string $param)\n{\n    return $param;\n}\n$a = myFunction(null); // TypeError: myFunction(): Argument #1 ($param) must be of type string, null given\n```\n\nIf a function has a return type, it won't accept null value either:\n\n```php\nfunction myFunction(): string\n{\n    return null;\n}\n$a = myFunction(); // TypeError: myFunction(): Return value must be of type string, null returned\n```\n\nYou can make a type declaration explicitly nullable:\n\n```php\nfunction myFunction(?string $param)\n{\n    return $param;\n}\n$a = myFunction(null);\n// $a = null\n```\n\nor with a union type:\n\n```php\nfunction myFunction(string|null $param)\n{\n    return $param;\n}\n$a = myFunction(null);\n// $a = null\n```\n\nIt also works with return type:\n\n```php\nfunction myFunction(?string $param): ?string\n{\n    return $param;\n}\n// or\nfunction myFunction(string|null $param): string|null\n{\n    return $param;\n}\n```\n\nBut void cannot be nullable:\n\n```php\nfunction myFunction(): ?void\n{\n   // some code\n} \n// PHP Fatal error:  Void type cannot be nullable\n```\n\nor\n\n```php\nfunction myFunction(): void|null\n{\n   // some code\n}\n// PHP Fatal error:  Void type cannot be nullable\n```\n\nYou can set a nullable type to a class property:\n\n```php\nClass Foo\n{\n    public int|null $bar;\n}\n$f = new Foo();\n$f-\u003ebar = null;\n$a = $f-\u003ebar;\n// $a = null\n```\n\n### Destructuring arrays\n\nYou can destructure arrays to pull out several elements into separate variables.\n\n#### Indexed array\n\n![php-version-40](https://shields.io/badge/php-\u003e=4.0-blue)\n\nConsidering an indexed array like :\n\n```php\n$array = ['foo', 'bar', 'baz'];\n```\n\nYou can destruct it using the list syntax:\n\n```php\nlist($a, $b, $c) = $array;\n\n// $a = 'foo'\n// $b = 'bar'\n// $c = 'baz'\n```\n\nOr since PHP 7.1, the shorthand syntax:\n\n```php\n[$a, $b, $c] = $array;\n\n// $a = 'foo'\n// $b = 'bar'\n// $c = 'baz'\n```\n\nYou can skip elements:\n\n```php\nlist(, , $c) = $array;\n\n// $c = 'baz'\n```\n\nOr since PHP 7.1, the shorthand syntax:\n\n```php\n[, , $c] = $array;\n\n// $c = 'baz'\n```\n\nWhen you try to destruct an index that doesn't exist in the given array, you'll get a warning:\n\n```php\nlist($a, $b, $c, $d) = $array; // PHP Warning:  Undefined array key 3\n\n// $a = 'foo'\n// $b = 'bar'\n// $c = 'baz'\n// $d = null;\n```\n\nYou can also swap variables with destructuring assignments, considering you have variable like:\n```php\n$a = 'foo';\n$b = 'bar';\n```\n\nSo if you want to swap `$a` and `$b` instead of using a temporary variable like this:\n\n```php\n$temp = $a;\n$a = $b;\n$b = $temp;\n\n// $a = 'bar'\n// $b = 'foo'\n```\n\nYou can swap it using the list syntax:\n\n```php\nlist($a, $b) = [$b, $a];\n\n// $a = 'bar'\n// $b = 'foo'\n```\n\nOr since PHP 7.1, the shorthand syntax:\n\n```php\n[$a, $b] = [$b, $a];\n\n// $a = 'bar'\n// $b = 'foo'\n```\n\n#### Associative array\n\n![php-version-71](https://shields.io/badge/php-\u003e=7.1-blue)\n\nConsidering an associative array (string-keyed) like :\n\n```php\n$array = [\n    'foo' =\u003e 'value1',\n    'bar' =\u003e 'value2',\n    'baz' =\u003e 'value3',\n];\n```\n\nPrevious list syntax won't work with an associative array, and you'll get a warning:\n\n```php\nlist($a, $b, $c) = $array; // PHP Warning:  Undefined array key 0 ...\n\n// $a = null\n// $b = null\n// $c = null\n```\n\nBut since PHP 7.1, you can destruct it with another syntax based on keys:\n\n```php\nlist('foo' =\u003e $a, 'bar' =\u003e $b, 'baz' =\u003e $c) = $array;\n\n// $a = 'value1'\n// $b = 'value2'\n// $c = 'value3'\n```\n\nOr the shorthand syntax:\n\n```php\n['foo' =\u003e $a, 'bar' =\u003e $b, 'baz' =\u003e $c] = $array;\n\n// $a = 'value1'\n// $b = 'value2'\n// $c = 'value3'\n```\n\nYou can also destruct only a portion of the array (The order doesn't matter):\n\n```php\n['baz' =\u003e $c, 'foo' =\u003e $a] = $array;\n\n// $a = 'value1'\n// $c = 'value3'\n```\n\nWhen you try to destruct a key that doesn't exist in the given array, you'll get a warning:\n\n```php\nlist('moe' =\u003e $d) = $array; // PHP Warning:  Undefined array key \"moe\"\n\n// $d = null\n```\n\n### Null Coalescing\n\n![php-version-70](https://shields.io/badge/php-\u003e=7.0-blue)\n\nSince PHP 7.0, you can use the null coalescing operator to provide a fallback when a property is null with no error nor warning:\n\n```php\n$a = null;\n$b = $a ?? 'fallback';\n\n// $b = 'fallback'\n```\n\nIt is equivalent to:\n\n```php\n$a = null;\n$b = isset($a) ? $a : 'fallback';\n// $b = 'fallback'\n```\n\nIt also works when property is undefined:\n\n```php\n$a = $undefined ?? 'fallback';\n\n// $a = 'fallback'\n```\n\nEvery other value of the property won't trigger the fallback:\n\n```php\n'' ?? 'fallback'; // ''\n0 ?? 'fallback'; // 0\nfalse ?? 'fallback'; // false\n```\n\nYou can chain null coalescing multiple times:\n\n```php\n$a = null;\n$b = null;\n$c = $a ?? $b ?? 'fallback';\n// $c = 'fallback'\n```\n\n#### Elvis operator\n\n![php-version-53](https://shields.io/badge/php-\u003e=5.3-blue)\n\nIt should not be confused with the shorthand ternary operator (aka the elvis operator), which was introduced in PHP 5.3:\n\n```php\n$a = null;\n$b = $a ?: 'fallback';\n\n// $b = 'fallback'\n```\n\nThe shorthand ternary operator is equivalent to:\n\n```php\n$a = null;\n$b = $a ? $a : 'fallback';\n// $b = 'fallback'\n```\n\nResult between null coalescing and elvis operator can be similar, but also different for some specific values:\n\n```php\n'' ?: 'fallback'; // 'fallback'\n0 ?: 'fallback'; // 'fallback'\nfalse ?: 'fallback'; // 'fallback'\n```\n\n#### Null coalescing on array\n\nIf array key exists, then fallback isn't triggered:\n\n```php\n$a = ['foo' =\u003e 'bar'];\n$b = $a['foo'] ?? 'fallback';\n\n// $b = 'bar'\n```\n\nBut when array doesn't exist, fallback is triggered with no error nor warning:\n\n```php\n$a = null;\n$b = $a['foo'] ?? 'fallback';\n\n// $b = 'fallback'\n```\n\nOr array property is undefined, fallback is triggered with no error nor warning:\n\n```php\n$b = $undefined['foo'] ?? 'fallback';\n\n// $b = 'fallback'\n```\n\nWhen array exist but key can't be found in the given array, fallback is triggered with no error nor warning:\n\n```php\n$a = [];\n$b = $a['foo'] ?? 'fallback';\n\n// $b = 'fallback'\n```\n\nIt also works with indexed arrays:\n\n```php\n$a = ['foo'];\n\n// reminder: $a[0] = 'foo'\n\n$b = $a[1] ?? 'fallback';\n\n// $b = 'fallback'\n```\n\nIt also works with nested arrays. If nested array key exists, then fallback isn't triggered:\n\n```php\n$a = [\n   'foo' =\u003e [\n      'bar' =\u003e 'baz'\n   ]\n];\n$b = $a['foo']['bar'] ?? 'fallback';\n\n// $b = 'baz'\n```\n\nBut when nested key can't be found in the given array, fallback is triggered with no error nor warning:\n\n```php\n$a = [\n   'foo' =\u003e [\n      'bar' =\u003e 'baz'\n   ]\n];\n$b = $a['foo']['qux'] ?? 'fallback';\n\n// $b = 'fallback'\n```\n\n#### Null coalescing on object\n\nYou can also use null coalescing operator with object.\n\n##### Object's attribute\n\nIf object's attribute exists, then fallback isn't triggered:\n\n```php\n$a = (object)[\n    'foo' =\u003e 'bar'\n];\n$b = $a-\u003efoo ?? 'fallback';\n\n// $b = 'bar'\n```\n\nBut when object's attribute can't be found, fallback is triggered with no error nor warning:\n\n```php\n$a = (object)[\n    'foo' =\u003e 'bar'\n];\n$b = $a-\u003ebaz ?? 'fallback';\n\n// $b = 'fallback'\n```\n\n##### Object's method\n\nYou can also use the null coalescing operator on call to an object's method. If the given method exists, then fallback isn't triggered:\n\n```php\nclass Foo\n{\n    public function bar()\n    {\n        return 'baz';\n    }\n}\n\n$a = new Foo();\n$b = $a-\u003ebar() ?? 'fallback';\n\n// $b = 'baz'\n```\n\nBut when object's method returns null, fallback is triggered with no error nor warning:\n\n```php\nclass Foo\n{\n    public function bar()\n    {\n        return null;\n    }\n}\n\n$a = new Foo();\n$b = $a-\u003ebar() ?? 'fallback';\n\n// $b = 'fallback'\n```\n\nIf object's method can't be found, null coalescing won't work and you'll get an error:\n\n```php\nclass Foo\n{\n    public function bar()\n    {\n        return 'baz';\n    }\n}\n\n$a = new Foo();\n$b = $a-\u003ebaz() ?? 'fallback'; // PHP Error:  Call to undefined method baz()\n```\n\n##### Chained method\n\nWhen using chained methods on object and an intermediary element can't be found, null coalescing won't work and you'll get an error:\n\n```php\nclass Foo\n{\n    public function bar()\n    {\n        return (object)[];\n    }\n}\n\n$a = new Foo();\n$b = $a-\u003ebar()-\u003ebaz() ?? 'fallback'; // PHP Error:  Call to undefined method baz()\n```\n\n#### Null Coalescing Assignment operator\n\n![php-version-74](https://shields.io/badge/php-\u003e=7.4-blue)\n\nYou can set a default value to a property when it is null:\n\n```php\n$a = null;\n$a = $a ?? 'foo';\n// $a = 'foo'\n```\n\nSince PHP 7.4, you can use the null coalescing assignment operator to do the same:\n\n```php\n$a = null;\n$a ??= 'foo';\n// $a = 'foo'\n```\n\n### Nullsafe operator\n\n![php-version-80](https://shields.io/badge/php-\u003e=8.0-blue)\n\nWhen trying to read a property or calling a method on null, you'll get a warning and an error:\n\n```php\n$a = null;\n$b = $a-\u003efoo; // PHP Warning:  Attempt to read property \"foo\" on null\n// $b = null\n\n$c = $a-\u003efoo(); // PHP Error:  Call to a member function foo() on null\n```\n\nWith the nullsafe operator, you can do both without warning nor error:\n\n```php\n$a = null;\n$b = $a?-\u003efoo;\n// $b = null\n$c = $a?-\u003efoo();\n// $c = null\n```\n\nYou can chain multiple nullsafe operators:\n\n```php\n$a = null;\n$b = $a?-\u003efoo?-\u003ebar;\n// $b = null\n$c = $a?-\u003efoo()?-\u003ebar();\n// $c = null\n```\n\nAn expression is short-circuited from the first null-safe operator that encounters null:\n\n```php\n$a = null;\n$b = $a?-\u003efoo-\u003ebar-\u003ebaz();\n// $b = null\n```\n\nNullsafe operator has no effect if the target is not null:\n\n```php\n$a = 'foo';\n$b = $a?-\u003ebar; // PHP Warning:  Attempt to read property \"bar\" on string\n// $b = null\n$c = $a?-\u003ebaz(); // PHP Error:  Call to a member function baz() on string\n```\n\nNullsafe operator can't handle arrays properly but still can have some effect:\n\n```php\n$a = [];\n$b = $a['foo']-\u003ebar;\n// PHP Warning:  Undefined array key \"foo\"\n// PHP Warning:  Attempt to read property \"bar\" on null\n// $b = null\n\n$c = $a['foo']?-\u003ebar; // PHP Warning:  Undefined array key \"foo\"\n// $c = null\n\n$d = $a['foo']-\u003ebar();\n// PHP Warning:  Undefined array key \"foo\"\n// PHP Error:  Call to a member function bar() on null\n\n$e = $a['foo']?-\u003ebar(); // PHP Warning:  Undefined array key \"foo\"\n// $e = null\n```\n\nYou cannot use the nullsafe operator to write, it is read only:\n\n```php\n$a = null;\n$a?-\u003efoo = 'bar'; // PHP Fatal error:  Can't use nullsafe operator in write context\n```\n\n### Spread operator\n\n#### Variadic parameter\n\n![php-version-56](https://shields.io/badge/php-\u003e=5.6-blue)\n\nSince PHP 5.6 (~ august 2014), you can add a variadic parameter to any function that let you use an argument lists with variable-length:\n\n```php\nfunction countParameters(string $param, string ...$options): int\n{\n\n    foreach ($options as $option) {\n        // you can iterate on $options\n    }\n \n    return 1 + count($options);\n}\n\ncountParameters('foo'); // 1\ncountParameters('foo', 'bar'); // 2\ncountParameters('foo', 'bar', 'baz'); // 3\n```\n\nVariadic parameter should always be the last parameter declared:\n\n```php\nfunction countParameters(string ...$options, string $param)\n{ \n   // some code\n}\n// PHP Fatal error: Only the last parameter can be variadic\n```\n\nYou can have only one variadic parameter:\n\n```php\nfunction countParameters(string ...$options, string ...$moreOptions)\n{ \n   // some code\n}\n// PHP Fatal error: Only the last parameter can be variadic\n```\n\nIt can't have a default value:\n\n```php\nfunction countParameters(string $param, string ...$options = [])\n{\n   // some code\n}\n// PHP Parse error: Variadic parameter cannot have a default value\n```\n\nWhen not typed, it accepts any value:\n\n```php\nfunction countParameters(string $param, ...$options): int\n{\n    return 1 + count($options);\n}\n\n$a = countParameters('foo', null, [], true);\n// $a = 4\n```\n\nWhen typed, you have to use properly typed values:\n\n```php\nfunction countParameters(string $param, string ...$options): int\n{\n    return 1 + count($options);\n}\n\ncountParameters('foo', null);\n// TypeError: countParameters(): Argument #2 must be of type string, null given\n\ncountParameters('foo', []);\n// TypeError: countParameters(): Argument #2 must be of type string, array given\n```\n\n#### Argument unpacking\n\n![php-version-56](https://shields.io/badge/php-\u003e=5.6-blue)\n\nArrays and traversable objects can be unpacked into argument lists when calling functions by using the spread operator:\n\n```php\nfunction add(int $a, int $b, int $c): int\n{\n    return $a + $b + $c;\n}\n$array = [2, 3];\n$r = add(1, ...$array);\n\n// $r = 6\n```\n\nThe given array can have more elements than needed:\n\n```php\nfunction add(int $a, int $b, int $c): int\n{\n    return $a + $b + $c;\n}\n$array = [2, 3, 4, 5];\n$r = add(1, ...$array);\n\n// $r = 6\n```\n\nThe given array can't have lesser elements than needed:\n\n```php\nfunction add(int $a, int $b, int $c): int\n{\n    return $a + $b + $c;\n}\n$array = [2];\n$r = add(1, ...$array); // TypeError: Too few arguments to function add(), 2 passed\n```\n\nExcept when some function arguments have a default value:\n\n```php\nfunction add(int $a, int $b, int $c = 0): int\n{\n    return $a + $b + $c;\n}\n$array = [2];\n$r = add(1, ...$array);\n// $r = 3\n```\n\nIf an argument is typed and the passed value does not match the given type, you'll get an error:\n\n```php\nfunction add(int $a, int $b, int $c): int\n{\n    return $a + $b + $c;\n}\n$array = ['foo', 'bar'];\n$r = add(1, ...$array); // TypeError: add(): Argument #2 ($b) must be of type int, string given\n```\n\nSince PHP 8.0, it is possible to unpack an associative array (string-keyed) as it will use [named arguments](#unpacking-named-arguments).\n\n#### Array unpacking\n\n##### Indexed array\n\n![php-version-74](https://shields.io/badge/php-\u003e=7.4-blue)\n\nWhen you want to merge multiple arrays, you generally use `array_merge`:\n\n```php\n$array1 = ['baz'];\n$array2 = ['foo', 'bar'];\n\n$array3 = array_merge($array1, $array2);\n// $array3 = ['baz', 'foo', 'bar']\n```\n\nBut since PHP 7.4, you can unpack indexed arrays, with spread operator:\n\n```php\n$array1 = ['foo', 'bar'];\n$array2 = ['baz', ...$array1];\n// $array2 = ['baz', 'foo', 'bar']\n```\n\nElements will be merged in the order they are passed:\n\n```php\n$array1 = ['foo', 'bar'];\n$array2 = ['baz', ...$array1, \"qux\"];\n// $array2 = ['baz', 'foo', 'bar', \"qux\"]\n```\n\nIt doesn't do any deduplication:\n\n```php\n$array1 = ['foo', 'bar'];\n$array2 = ['foo', ...$array1];\n// $array2 = ['foo', 'foo', 'bar']\n```\n\nYou can unpack multiple arrays at once:\n\n```php\n$array1 = ['foo', 'bar'];\n$array2 = ['baz'];\n$array3 = [ ...$array1, ...$array2];\n// $array3 = ['foo', 'bar', 'baz']\n```\n\nYou can unpack the same array multiple times:\n\n```php\n$array1 = ['foo', 'bar'];\n$array2 = [ ...$array1, ...$array1];\n// $array2 = ['foo', 'bar', 'foo', 'bar']\n```\n\nYou can unpack an empty array with no error nor warning:\n\n```php\n$array1 = [];\n$array2 = ['foo', ...$array1];\n// $array2 = ['foo']\n```\n\nYou can unpack an array that has not been previously stored in a property:\n\n```php\n$array1 = [...['foo', 'bar'], 'baz'];\n// $array1 = ['foo', 'bar', 'baz']\n```\n\nUnpacking only works with arrays (or objects inplementing Traversable interface). If you try to unpack any other value, such as null, you'll get an error: \n\n```php\n$array1 = null;\n$array2 = ['foo', ...$array1]; // PHP Error:  Only arrays and Traversables can be unpacked\n```\n\nYou can unpack the result of a function/method:\n\n```php\nfunction getArray(): array\n{\n    return ['foo', 'bar'];\n}\n\n$array = [...getArray(), 'baz']; \n// $array = ['foo', 'bar', 'baz']\n```\n\n##### Associative array\n\n![php-version-81](https://shields.io/badge/php-\u003e=8.1-blue)\n\nSince php 8.1, you can unpack associative array (string-keyed):\n\n```php\n$array1 = ['foo' =\u003e 'bar'];\n$array2 = [\n   'baz' =\u003e 'qux', \n   ...$array1\n];\n// $array2 = ['baz' =\u003e 'qux', 'foo' =\u003e 'bar',]\n```\n\nYou can unpack array with an already existing key:\n\n```php\n$array1 = ['foo' =\u003e 'bar'];\n$array2 = [\n   'foo' =\u003e 'baz', \n   ...$array1\n];\n// $array2 = ['foo' =\u003e 'bar',]\n```\n\nYou can unpack an empty array without error nor warning:\n\n```php\n$array1 = [];\n$array2 = [\n   ...$array1,\n   ...[]\n];\n// $array2 = []\n```\n\n### Named arguments\n\n![php-version-80](https://shields.io/badge/php-\u003e=8.0-blue)\n\nSince PHP 8.0, it is possible to pass in arguments by name instead of their position.\n\nConsidering a function like this:\n\n```php\nfunction concat(string $first, string $second): string\n{\n    return $first . ' ' . $second;\n}\n$a = concat('foo', 'bar');\n// $a = 'foo bar'\n```\n\nYou can have the same result with the named argument syntax:\n\n```php\n$a = concat(first: 'foo', second: 'bar');\n// $a = 'foo bar'\n```\n\nYou can call it with arguments in a different order:\n\n```php\n$a = concat(second: 'bar', first: 'foo');\n// $a = 'foo bar'\n```\n\nYou can skip optional parameters:\n\n```php\nfunction orGate(bool $option1 = false, bool $option2 = false, bool $option3 = false): bool\n{\n   return $option1 || $option2 || $option3;\n}\n$a = orGate(option3: true);\n// $a = true\n```\n\nBut you cannot skip a mandatory argument:\n\n```php\n$a = concat(second: 'bar');\n// TypeError: concat(): Argument #1 ($first) not passed\n```\n\nYou cannot include some extra arguments:\n\n```php\n$a = concat(first: 'foo', second: 'bar', third: 'baz');\n// PHP Error:  Unknown named parameter $third\n```\n\nNamed arguments also work with object constructor:\n\n```php\nClass Foo\n{\n    public function __construct(\n        public string $first,\n        public string $second\n    ) {}\n    \n}\n$f = new Foo(first: 'bar', second: 'baz');\n```\n\n#### Named variadics\n\nYou can use named arguments with a variadic parameter:\n\n```php\nfunction showParams(string ...$params): array\n{\n    return $params;\n}\n$a = showParams(first: 'foo', second: 'bar', third: 'baz');\n// $a = [\"first\" =\u003e \"foo\", \"second\" =\u003e \"bar\", \"third\" =\u003e \"baz\"]\n```\n\n#### Unpacking named arguments\n\nYou can unpack an associative array as named arguments if keys match arguments names:\n\n```php\nfunction add(int $a, int $b, int $c): int\n{\n    return $a + $b + $c;\n}\n$array = [\n    \"b\" =\u003e 2,\n    \"c\" =\u003e 3\n];\n$r = add(1, ...$array);\n// $r = 6\n```\n\nOrder of the elements in the associative array doesn't matter:\n\n```php\nfunction add(int $a, int $b, int $c): int\n{\n    return $a + $b + $c;\n}\n$array = [\n    \"c\" =\u003e 3,\n    \"b\" =\u003e 2,\n];\n$r = add(1, ...$array);\n// $r = 6\n```\n\nIf a key doesn't match an argument's name, you'll get an error:\n\n```php\nfunction add(int $a, int $b, int $c): int\n{\n    return $a + $b + $c;\n}\n$array = [\n    \"b\" =\u003e 2,\n    \"c\" =\u003e 3,\n    \"d\" =\u003e 4,\n];\n$r = add(1, ...$array); // PHP Error:  Unknown named parameter $d\n```\n\n#### External resource\n\n- [Named arguments in depth on stitcher's blog](https://stitcher.io/blog/php-8-named-arguments)\n- [Named Parameters on PHP.Watch](https://php.watch/versions/8.0/named-parameters)\n\n### Short closures\n\n![php-version-74](https://shields.io/badge/php-\u003e=7.4-blue)\n\nShort closures, also called arrow functions, are an alternative way of writing [anonymous functions](https://www.php.net/manual/en/functions.anonymous.php) in a shorter syntax. The main goal of short closures is to reduce verbosity when it is possible : if there is only a single expression.\n\nHere is an example of a simple closure with only one expression :\n\n```php\n$foo = function ($bar) {\n    return $bar + 1;\n}\n$a = $foo(1);\n// $a = 2\n```\n\nYou can write the same function with a short closure :\n\n```php\n$foo = fn ($bar) =\u003e $bar + 1;\n$a = $foo(1);\n// $a = 2\n```\n\nYou cannot give a name to a short closure :\n\n```php\nfn foo($bar) =\u003e $bar + 1;\n// PHP Parse error: Syntax error, unexpected T_STRING, expecting '('\n```\n\nYou can use short closure as function parameter. For example as a \"callable\" parameter in PHP's [array_reduce](https://www.php.net/manual/en/function.array-reduce.php):\n\n```php\n$myArray = [10,20,30];\n\n$total = array_reduce($myArray, fn ($carry, $item) =\u003e $carry + $item, 0);\n// $total = 60\n```\n\nType hinting is allowed as in a normal function :\n\n```php\nfn (int $foo): int =\u003e $foo;\n```\n\nYou don't need to use the `return` keyword as it is not allowed here :\n\n```php\nfn ($foo) =\u003e return $foo;\n// PHP Parse error: Syntax error, unexpected T_RETURN\n```\n\n#### Outer scope\n\nThe short closure doesn't require the `use` keyword to be able to access properties from the outer scope :\n\n```php\n$bar = 10;\n$baz = fn ($foo) =\u003e $foo + $bar;\n$a = $baz(1);\n//$a = 11\n```\n\nThe keyword `use` is not allowed :\n\n```php\n$bar = 10;\nfn ($foo) use ($bar) =\u003e $foo + $bar;\n// PHP Parse error: Syntax error, unexpected T_USE, expecting T_DOUBLE_ARROW\n```\n\nYou could use `$this` as in any other function :\n\n```php\nfn () =\u003e $this-\u003efoo + 1;\n```\n\n### Match expression\n\n![php-version-80](https://shields.io/badge/php-\u003e=8.0-blue)\n\nSince PHP 8.0, there is a new `match` syntax similar to the `switch` syntax. As each matching case must only contain one expression, it can't be used and replace a switch statement in every situation. It is significantly shorter and easier to read though.\n\nThe match expression always returns a value. Each condition only allows a single expression, and it immediately returns the value and will not fall-through following conditions without an explicit `break` statement:\n\n```php\n$foo = 'baz';\n$a = match($foo) {\n    'bar' =\u003e 1,\n    'baz' =\u003e 2,\n    'qux' =\u003e 3,\n}\n// $a = 2\n```\n\nIt throws an exception when the value can't match:\n\n```php\n$foo = 'qux';\n$a = match($foo) {\n    'bar' =\u003e 1,\n    'baz' =\u003e 2,\n}\n// PHP Error:  Unhandled match value of type string\n```\n\nBut it supports a default condition:\n\n```php\n$foo = 'qux';\n$a = match($foo) {\n    'bar' =\u003e 1,\n    'baz' =\u003e 2,\n    default =\u003e 3,\n}\n// $a = 3\n```\n\nIt allows multiple conditions in a single arm:\n\n```php\n$foo = 'bar';\n$a = match($foo) {\n    'bar', 'baz' =\u003e 1,\n    default =\u003e 2,\n}\n// $a = 1\n```\n\nIt does strict type-safe comparison without type coercion (it's like using `===` instead of `==`):\n\n```php\nfunction showType($param) {\n    return match ($param) {\n        1 =\u003e 'Integer',\n        '1' =\u003e 'String',\n        true =\u003e 'Boolean',\n    };\n}\n\nshowType(1); // \"Integer\"\nshowType('1'); // \"String\"\nshowType(true); // \"Boolean\"\n```\n\n#### External resource\n\n- [Match expression on PHP.Watch](https://php.watch/versions/8.0/match-expression)\n\n### Stringable interface\n\n![php-version-80](https://shields.io/badge/php-\u003e=8.0-blue)\n\nSince PHP 8.0, there is a new interface named `Stringable`, that indicates a class has a `__toString()` magic method. PHP automatically adds the Stringable interface to all classes that implement that method.\n\n```php\ninterface Stringable {\n    public function __toString(): string;\n}\n```\n\nWhen you define a parameter with `Stringable` type, it will check that the given class implements the `Stringable` interface:\n\n```php\nclass Foo {\n    public function __toString(): string {\n        return 'bar';\n    }\n}\n\nfunction myFunction(Stringable $param): string {\n    return (string) $param;\n}\n$a = myFunction(new Foo);\n// $a = 'bar'\n```\n\nIf a given class doesn't implement `__toString()`, you'll get an error:\n\n```php\nclass Foo {\n}\n\nfunction myFunction(Stringable $param): string {\n    return (string) $param;\n}\n$a = myFunction(new Foo);\n// TypeError: myFunction(): Argument #1 ($param) must be of type Stringable, Foo given\n```\n\nA stringable type doesn't accept string:\n\n```php\nfunction myFunction(Stringable $param): string {\n    return (string) $param;\n}\n$a = myFunction('foo');\n// TypeError: myFunction(): Argument #1 ($param) must be of type Stringable, string given\n```\n\nOf course, to accept both string and Stringable, you can use a union type:\n\n```php\nfunction myFunction(string|Stringable $param): string {\n    return (string) $param;\n}\n```\n\n### Enums\n\n![php-version-81](https://shields.io/badge/php-\u003e=8.1-blue)\n\nAn Enum defines a new type, which has a fixed, limited number of possible legal values.\n\n```php\nenum Status\n{\n    case DRAFT;\n    case PUBLISHED;\n    case ARCHIVED;\n}\n```\n\nIn an Enum, each case definition is case-sensitive. Historically, in PHP we generally represent \"constants\" with uppercase to distinguish them from normal variables, so it makes sense to stick to uppercase notation for enum cases. But note that this will work fine and define 3 different cases:\n\n```php\nenum MyEnum\n{\n    case FOO;\n    case foo;\n    case Foo;\n}\n```\n\nNow you can compare easily enums with type safe operator `===`:\n\n```php\n$statusA = Status::PENDING;\n\nif ($statusA === Status::PENDING) {\n    // true\n}\n```\n\nAlso an enum behaves like a traditional PHP object:\n\n```php\n$statusA = Status::PENDING;\n$statusB = Status::PENDING;\n$statusC = Status::ARCHIVED;\n\n$statusA === $statusB; // true\n$statusA === $statusC; // false\n$statusC instanceof Status; // true\n```\n\nYou can use Enum to enforce types:\n\n```php\nfunction myFunction(Status $param)\n{\n    return $param;\n}\n$a = myFunction(Status::DRAFT);\n// $a = Status::DRAFT\n$b = myFunction('foo'); // TypeError: myFunction(): Argument #1 ($param) must be of type Status, string given\n```\n\n####  Enum methods\n\nYou can define methods with an Enum :\n\n```php\nenum Status\n{\n    case DRAFT;\n    case PUBLISHED;\n    \n    public function label(): string\n    {\n        return match($this) \n        {\n            Status::DRAFT =\u003e 'Not ready...',   \n            Status::PUBLISHED =\u003e 'Published !',   \n        };\n    }\n}\n```\n\nthen you can use methods on any enum instance:\n\n```php\n$a = Status::DRAFT;\n$a-\u003elabel(); // 'Not ready...'\n```\n\n#### Backed values\n\nSometimes you need to assign a proper value to each enum case (ex: to store it in a database, comparison, etc). You should define the type of the back value. Here is an example with a backed value defined as an `int` :\n\n```php\nenum HttpStatus: int\n{\n    case OK = 200;\n    case NOT_FOUND = 404;\n    case INTERNAL_SERVER_ERROR = 500;\n}\n```\n\nAnd here is an example of a backed value defined as a `string`:\n\n```php\nenum Status: string\n{\n    case DRAFT = 'draft';\n    case PUBLISHED = 'published';\n}\n```\n\n#### External resource\n\n- [Enums manual on PHP official documentation](https://www.php.net/manual/en/language.enumerations.php)\n- [Enums on PHP.Watch](https://php.watch/versions/8.0/match-expression)\n- [Enums style guide on stitcher's blog](https://stitcher.io/blog/php-enum-style-guide)\n\n### Multiple lines string syntax\n\n![php-version-73](https://shields.io/badge/php-\u003e=7.3-blue)\n\nWhen you want to define a string value that contains multiple lines, you generally use double quotes and escape line breaks:\n\n```php\n$string = \"Hello\\nWorld\";\n```\n\nSince PHP 7.3, there is a new option for specifying a string value over multiple lines. By placing it between an opening identifier and a closing identifier:\n\n```php\n$string = \u003c\u003c\u003cIDENTIFIER\nHello \nWorld\nIDENTIFIER;\n```\n\nYou can use any identifier you want, but it must be the same at the beginning and at the end of the string. It can't be a variable. \n\nThe closing identifier can be followed by other code on the same line:\n\n```php\n$array = ['foo', 'bar', \u003c\u003c\u003cIDENTIFIER\n  hello\n  world\n  IDENTIFIER, 'baz', 'qux',\n];\n```\n\n#### External resource\n\n- [Relaxed heredoc and nowdoc on PHP.Watch](https://php.watch/versions/7.3/relaxed-heredoc-nowdoc)\n- [Heredoc nowdoc on andycarter.dev's blog](https://andycarter.dev/blog/what-are-php-heredoc-nowdoc)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmknstd%2Fmodern-php-cheatsheet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsmknstd%2Fmodern-php-cheatsheet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmknstd%2Fmodern-php-cheatsheet/lists"}