├── .gitignore
├── content
├── 300-syntax-changes.md
├── 200-new-functions.md
├── 500-internal-improvements.md
├── 205-fdiv.md
├── 301-object-class.md
├── 100-major-new-changes.md
├── 303-trailing-comma-in-parameter-list.md
├── 201-str_contains.md
├── 302-non-capturing-catches.md
├── 111-stringable-interface.md
├── 112-validate-abstract_trait_methods.md
├── 202-str_srarts-ends_with.md
├── 203-get_debug_type.md
├── 108-mixed-types.md
├── 105-constructor-promotion.md
├── 103-named-arguments.md
├── 106-static-return.md
├── 109-throw-expression.md
├── 107-match.md
├── 104-attributes.md
├── 102-nullsafe-operator.md
├── 001-introduction.md
├── 400-breaking-changes.md
└── 101-union-types.md
├── assets
├── cover.jpg
└── theme-light.html
├── book
└── whats-new-in-php8.pdf
├── ibis.php
├── composer.json
├── composer.lock
├── examples
├── Match
│ ├── Match2.php
│ ├── Match3.php
│ ├── Switch3.php
│ ├── Switch2.php
│ ├── Switch4.php
│ ├── Match.php
│ └── Switch.php
├── Attributes
│ ├── TestClass.php
│ ├── TestAttribute.php
│ └── testing_attributes.php
├── UnionTypes
│ ├── UnionTypes.php
│ ├── Overloading.php
│ ├── TypeHinting.php
│ ├── DynamicTypes.php
│ └── TypeHinting2.php
├── Stringable
│ └── stringable.php
├── NewFunctions
│ ├── fdiv.php
│ ├── str_starts_ends_with.php
│ └── get_debug_type.php
├── ConstructorPromotion
│ └── Point.php
├── NamedArguments
│ ├── NamedArguments.php
│ └── NamedArguments2.php
├── StaticReturn
│ ├── LateStaticBinding.php
│ └── StaticReturnType.php
├── NullsafeOperator
│ ├── Nullsafe.php
│ └── NoNullsafePhp7.php
└── changelog.md
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor/
2 | /export/
3 |
--------------------------------------------------------------------------------
/content/300-syntax-changes.md:
--------------------------------------------------------------------------------
1 | # Syntax changes
--------------------------------------------------------------------------------
/assets/cover.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kapilsharma/WhatsNewInPHP8/HEAD/assets/cover.jpg
--------------------------------------------------------------------------------
/book/whats-new-in-php8.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kapilsharma/WhatsNewInPHP8/HEAD/book/whats-new-in-php8.pdf
--------------------------------------------------------------------------------
/content/200-new-functions.md:
--------------------------------------------------------------------------------
1 | # New functions
2 |
3 | PHP 8 introduced lot of new internal functions. Let's see them one by one.
4 |
5 |
--------------------------------------------------------------------------------
/ibis.php:
--------------------------------------------------------------------------------
1 | 'What\'s new in PHP 8',
5 | 'author' => 'Kapil Sharma',
6 | 'fonts' => [],
7 | ];
8 |
--------------------------------------------------------------------------------
/content/500-internal-improvements.md:
--------------------------------------------------------------------------------
1 | # Internal Improvements
2 |
3 | ### JIT
4 |
5 | ### Inheritance with private methods
6 |
7 | ### `ext-json` always available
--------------------------------------------------------------------------------
/content/205-fdiv.md:
--------------------------------------------------------------------------------
1 | ## Function `fdiv`
2 |
3 | > RFC: Not able to find. PR: [https://github.com/php/php-src/pull/4769](https://github.com/php/php-src/pull/4769)
4 |
5 | PHP support `fmod` since PHP 4 and `intdiv` since version 7.
6 |
7 | In the same family of functions, PHP 8 added `fdiv`. It is similar to `intdiv` but works for float.
8 |
--------------------------------------------------------------------------------
/content/301-object-class.md:
--------------------------------------------------------------------------------
1 | ## Allowing `::class` on objects
2 |
3 | > RFC: [https://wiki.php.net/rfc/class_name_literal_on_object](https://wiki.php.net/rfc/class_name_literal_on_object)
4 |
5 | With PHP 8, we can now use `::class` on objects.
6 |
7 | Till PHP 7, `::class` was possible only on a class name. To get a class of an object, we need to call a function `get_class($object)`. Now with PHP 8, we can do `$object::class`.
8 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "kapsebook/php8",
3 | "description": "'What's new in PHP 8' Ebook and its code, distributed during Laravel-Nagpur meetup.",
4 | "type": "project",
5 | "license": "MIT",
6 | "authors": [
7 | {
8 | "name": "kapilsharma",
9 | "email": "kapil@kapilsharma.info"
10 | }
11 | ],
12 | "support": {
13 | "issues": "https://github.com/phpreboot/stopwatch/issues"
14 | },
15 | "autoload": {
16 | "psr-4": {"Phpreboot\\Php8\\": "examples"}
17 | },
18 | "require": {}
19 | }
20 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5 | "This file is @generated automatically"
6 | ],
7 | "content-hash": "980008fc0f8b1a83282a940649487e3b",
8 | "packages": [],
9 | "packages-dev": [],
10 | "aliases": [],
11 | "minimum-stability": "stable",
12 | "stability-flags": [],
13 | "prefer-stable": false,
14 | "prefer-lowest": false,
15 | "platform": [],
16 | "platform-dev": [],
17 | "plugin-api-version": "2.0.0"
18 | }
19 |
--------------------------------------------------------------------------------
/content/100-major-new-changes.md:
--------------------------------------------------------------------------------
1 | # Major new changes
2 |
3 | In this section, we will discuss new features introduced in PHP version 8.
4 |
5 | It is not a book to learn PHP but Kapil's notes while going through new PHP 8 features, converted into a form of EBook. I assume you already know PHP and its features available till PHP version 7.4. Thus, without going into much theory, let's dive directly into changes and their examples.
6 |
7 | Please note, in some sections, we will also discuss the history to understand features that came in older versions. It could be useful for those who are not aware or remember changes in PHP 7 or earlier versions. If you wish to skip the history, go to the title `Changes in PHP 8`.
8 |
9 |
--------------------------------------------------------------------------------
/content/303-trailing-comma-in-parameter-list.md:
--------------------------------------------------------------------------------
1 | ## Allow a trailing comma in the parameter list
2 |
3 | > RFC: [https://wiki.php.net/rfc/trailing_comma_in_parameter_list](https://wiki.php.net/rfc/trailing_comma_in_parameter_list)
4 |
5 | PHP already supported adding a comma after the last element of an array like
6 |
7 | ```php
8 | $exampleArray = [
9 | 'a',
10 | 'b',
11 | 'c', // Comma after lase element is allowed in PHP
12 | ];
13 | ```
14 |
15 | Now with PHP 8, it is also possible with the parameter list of a method.
16 |
17 | ```php
18 | new Uri(
19 | $scheme,
20 | $user,
21 | $pass,
22 | $host,
23 | $port,
24 | $path,
25 | $query,
26 | $fragment, // <-- Huh, this is allowed!
27 | );
28 | ```
29 |
--------------------------------------------------------------------------------
/examples/Match/Match2.php:
--------------------------------------------------------------------------------
1 | "Oh no!\n",
20 | false => "This is what I expected\n",
21 | };
--------------------------------------------------------------------------------
/examples/Match/Match3.php:
--------------------------------------------------------------------------------
1 | 'Number for 1 and 2' . PHP_EOL,
20 | 3, 4 => 'Number for 3 and 4' . PHP_EOL,
21 | };
22 |
--------------------------------------------------------------------------------
/content/201-str_contains.md:
--------------------------------------------------------------------------------
1 | ## Function `str_contains`
2 |
3 | PHP 8 added a new function `str_contains`, which checks if a string is contained in another string and returns a boolean value (true/false) whether or not the string was found.
4 |
5 | **Syntax**
6 |
7 | ```php
8 | str_contains ( string $haystack , string $needle ) : bool
9 | ```
10 |
11 | **Example**
12 |
13 | ```php
14 | str_contains("abc", "a"); // true
15 | str_contains("abc", "d"); // false
16 |
17 | // $needle is an empty string
18 | str_contains("abc", ""); // true
19 | str_contains("", ""); // true
20 | ```
21 |
22 | **Note from RFC:** As of PHP 8, the behaviour of "" in string search functions is well defined, and we consider "" to occur at every position in the string, including one past the end. As such, both of these will (or at least should) return true. The empty string is contained in every string.
23 |
--------------------------------------------------------------------------------
/examples/Match/Switch3.php:
--------------------------------------------------------------------------------
1 | getMessage());
10 | }
11 | ```
12 |
13 | In the above case, we are using `$ex`. However, even if we do not need to use an exception object, we still needed to define it in PHP 7 and earlier versions like
14 |
15 | ```php
16 | try {
17 | changeImportantData();
18 | } catch (PermissionException $ex) {
19 | echo "You don't have permission to do this";
20 | }
21 | ```
22 |
23 | With PHP 8, if we do not want to use a variable, we need not assign it to a variable. Example
24 |
25 | ```php
26 | try {
27 | changeImportantData();
28 | } catch (PermissionException) { // The intention is clear: exception details are irrelevant
29 | echo "You don't have permission to do this";
30 | }
31 | ```
32 |
--------------------------------------------------------------------------------
/examples/Attributes/TestClass.php:
--------------------------------------------------------------------------------
1 | testArgument = $testArgument;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/examples/UnionTypes/UnionTypes.php:
--------------------------------------------------------------------------------
1 | add(1, 2) . PHP_EOL;
28 | echo '1.1 + 2.2 = ' . $instance->add(1.1, 2.2) . PHP_EOL;
29 |
--------------------------------------------------------------------------------
/examples/Attributes/testing_attributes.php:
--------------------------------------------------------------------------------
1 | getAttributes();
26 |
27 | echo $classAttributes[0]->newInstance()->testArgument . PHP_EOL;
28 |
--------------------------------------------------------------------------------
/content/111-stringable-interface.md:
--------------------------------------------------------------------------------
1 | ## Stringable interface
2 |
3 | > RFC: [https://wiki.php.net/rfc/stringable](https://wiki.php.net/rfc/stringable)
4 |
5 | PHP 8 introduced a new interface `Stringable`.
6 |
7 | ```php
8 | interface Stringable
9 | {
10 | public function __toString(): string;
11 | }
12 | ```
13 |
14 | Usage example
15 |
16 | ```php
17 | class ClassWithToString
18 | {
19 | public function __toString(): string
20 | {
21 | return 'test string from ClassWithToString::__toString';
22 | }
23 | }
24 |
25 | function testStringable(string|Stringable $stringable) {
26 | echo "String is '$stringable'" . PHP_EOL;
27 | }
28 |
29 | testStringable(new ClassWithToString());
30 | testStringable('Notmal string');
31 | ```
32 |
33 | Output
34 |
35 | ```bash
36 | String is 'test string from ClassWithToString::__toString'
37 | String is 'Normal string'
38 | ```
39 |
40 | Please note, we have not used `implements Stringable` in our class `ClassWithToString`. PHP 8 automatically add `implements Stringable` to any class that have `__toString()` method.
41 |
--------------------------------------------------------------------------------
/examples/Stringable/stringable.php:
--------------------------------------------------------------------------------
1 | 'Your favorite colour is Red.' . PHP_EOL,
22 | 'blue' => 'Your favorite colour is Blue.' . PHP_EOL,
23 | 'green' => 'Your favorite colour is Green.' . PHP_EOL,
24 | default => 'You do not like any primary colour.' . PHP_EOL,
25 | };
26 | }
27 |
28 | printFavoriteColor('Yellow');
29 |
--------------------------------------------------------------------------------
/examples/NewFunctions/fdiv.php:
--------------------------------------------------------------------------------
1 | x, $this->y, $this->z)" . PHP_EOL;
31 | }
32 | }
33 |
34 | $point = new Point(1, 2.2);
35 | $point->print();
36 |
--------------------------------------------------------------------------------
/content/112-validate-abstract_trait_methods.md:
--------------------------------------------------------------------------------
1 | ## Validation for abstract trait methods
2 |
3 | > RFC: [https://wiki.php.net/rfc/abstract_trait_method_validation](https://wiki.php.net/rfc/abstract_trait_method_validation)
4 |
5 | PHP 8 will strictly validate the signature of a trait's abstract method. Following code was possible in PHP 7:
6 |
7 | **File: None. (Taken from RFC)**
8 |
9 | ```php
10 | trait T {
11 | abstract public function test(int $x);
12 | }
13 |
14 | class C {
15 | use T;
16 |
17 | // Allowed in PHP 7 but not in PHP 8.
18 | public function test(string $x) {}
19 | }
20 | ```
21 |
22 | Now in PHP 8, above code is not valid because parameter type (int) of class `C` does not match with parameter type (string) of trait `T`. To make it work in PHP 8, parameter $x in C::test must be of type `int`. The following example will run in PHP 8
23 |
24 | **File: None. (Improved RFC example)**
25 |
26 | ```php
27 | trait T {
28 | abstract public function test(int $x);
29 | }
30 |
31 | class C {
32 | use T;
33 |
34 | // This will work in PHP 8.
35 | public function test(int $x) {}
36 | }
37 | ```
38 |
--------------------------------------------------------------------------------
/examples/UnionTypes/Overloading.php:
--------------------------------------------------------------------------------
1 | add(1, 2) . PHP_EOL;
33 | echo '1.1 + 2.2 = ' . $instance->add(1.1, 2.2) . PHP_EOL;
34 |
--------------------------------------------------------------------------------
/examples/UnionTypes/TypeHinting.php:
--------------------------------------------------------------------------------
1 | intType = $intType;
25 | }
26 |
27 | public function printValue()
28 | {
29 | echo 'Value of variable is ' . $this->intType . PHP_EOL;
30 | }
31 | }
32 |
33 | $instance = new TypeHinting(2);
34 | $instance->printValue();
35 |
36 | $instance->intType = 2.2;
37 | $instance->printValue();
38 |
--------------------------------------------------------------------------------
/examples/Match/Switch.php:
--------------------------------------------------------------------------------
1 | printUserInfo(
32 | name: 'Kapil',
33 | age: 38,
34 | occupation: 'Developer'
35 | );
36 |
37 | $instance->printUserInfo(
38 | occupation: 'Student',
39 | age: 8,
40 | name: 'Pari'
41 | );
42 |
--------------------------------------------------------------------------------
/content/202-str_srarts-ends_with.md:
--------------------------------------------------------------------------------
1 | ## Function `str_starts_with` and `str_ends_with`
2 |
3 | > RFC: [https://wiki.php.net/rfc/add_str_starts_with_and_ends_with_functions](https://wiki.php.net/rfc/add_str_starts_with_and_ends_with_functions)
4 |
5 | str_starts_with checks if a string begins with another string and returns a boolean value (true/false) whether it does.
6 |
7 | str_ends_with checks if a string ends with another string and returns a boolean value (true/false) whether it does.
8 |
9 | Syntax
10 |
11 | ```php
12 | str_starts_with ( string $haystack , string $needle ) : bool
13 | str_ends_with ( string $haystack , string $needle ) : bool
14 | ```
15 |
16 | Example (Taken from RFC)
17 |
18 | ```php
19 | $str = "beginningMiddleEnd";
20 | if (str_starts_with($str, "beg")) echo "printed\n";
21 | if (str_starts_with($str, "Beg")) echo "not printed\n";
22 | if (str_ends_with($str, "End")) echo "printed\n";
23 | if (str_ends_with($str, "end")) echo "not printed\n";
24 |
25 | // empty strings:
26 | if (str_starts_with("a", "")) echo "printed\n";
27 | if (str_starts_with("", "")) echo "printed\n";
28 | if (str_starts_with("", "a")) echo "not printed\n";
29 | if (str_ends_with("a", "")) echo "printed\n";
30 | if (str_ends_with("", "")) echo "printed\n";
31 | if (str_ends_with("", "a")) echo "not printed\n";
32 | ```
33 |
--------------------------------------------------------------------------------
/examples/StaticReturn/LateStaticBinding.php:
--------------------------------------------------------------------------------
1 | intType = $intType;
25 | }
26 |
27 | public function printValue()
28 | {
29 | echo 'Value of variable is ' . $this->intType . PHP_EOL;
30 | }
31 | }
32 |
33 | $instance = new TypeHinting2(2);
34 | $instance->printValue();
35 |
36 | $instance->intType = 2.2;
37 | $instance->printValue();
38 |
39 | $instance->intType = "3.5 is a float with some extra string";
40 | $instance->printValue();
41 |
42 | $instance->intType = "Now we have a string that can't be casted to int";
43 | $instance->printValue();
44 |
--------------------------------------------------------------------------------
/content/203-get_debug_type.md:
--------------------------------------------------------------------------------
1 | ## Function `get_debug_type`
2 |
3 | > RFC: [https://wiki.php.net/rfc/get_debug_type](https://wiki.php.net/rfc/get_debug_type)
4 |
5 | PHP already have `[gettype](https://www.php.net/manual/en/function.gettype.php)` since version 4. It returns the type of a variable for a given variable.
6 |
7 | PHP 8 introduced new function `get_debug_type`, it will be same as `gettype` + it will also work on objects. Example:
8 |
9 | **File: examples/NewFunctions/get_debug_type.php**
10 |
11 | ```php
12 | class ExampleClass { }
13 |
14 | $exampleClassOrBool1 = new ExampleClass();
15 | $exampleClassOrBool2 = false;
16 |
17 | echo "PHP 7 example" . PHP_EOL;
18 |
19 | echo "exampleClassOrBool1 is ";
20 | echo is_object($exampleClassOrBool1)
21 | ? get_class($exampleClassOrBool1)
22 | : gettype($exampleClassOrBool1);
23 | echo PHP_EOL;
24 |
25 | echo "exampleClassOrBool2 is ";
26 | echo is_object($exampleClassOrBool2)
27 | ? get_class($exampleClassOrBool2)
28 | : gettype($exampleClassOrBool2);
29 | echo PHP_EOL;
30 |
31 | echo "PHP 8 example" . PHP_EOL;
32 |
33 | echo "exampleClassOrBool1 is " . get_debug_type($exampleClassOrBool1) . PHP_EOL;
34 | echo "exampleClassOrBool2 is " . get_debug_type($exampleClassOrBool2) . PHP_EOL;
35 | ```
36 |
37 | Possible output for `get_debug_type` and `gettype` for different inputs can be seen at RFC.
38 |
--------------------------------------------------------------------------------
/examples/NewFunctions/str_starts_ends_with.php:
--------------------------------------------------------------------------------
1 | country = 'India';
26 | }
27 | }
28 |
29 | class User
30 | {
31 | protected $address;
32 |
33 | public function __construct()
34 | {
35 | // Adding dummy address for demonstration purpose
36 | $this->address = new Address();
37 | }
38 |
39 | public function getAddress()
40 | {
41 | return $this->address;
42 | }
43 | }
44 |
45 | class Session
46 | {
47 | public $user;
48 |
49 | public function __construct()
50 | {
51 | // Adding dummy user for demonstration purpose
52 | $this->user = new User();
53 | }
54 | }
55 |
56 | $session = new Session();
57 |
58 | $country = $session?->user?->getAddress()?->country;
59 |
60 | echo "Country is " . $country . PHP_EOL;
61 |
--------------------------------------------------------------------------------
/content/108-mixed-types.md:
--------------------------------------------------------------------------------
1 | ## Mixed types
2 |
3 | > RFC: [https://wiki.php.net/rfc/mixed_type_v2](https://wiki.php.net/rfc/mixed_type_v2)
4 |
5 | PHP introduced scalar types in PHP 7, nullable in 7.1, object in 7.2, and lastly, union types in 8.0 as discussed above. Many developers started using these features and now type-hinting their parameters and return types.
6 |
7 | However, we may still see this type-hinting is missing due to different reasons like:
8 |
9 |
10 | - the type is a specific type, but the `programmer forgot` to declare it.
11 | - the type is a specific type, but the programmer omitted it to keep `compatibility with an older PHP version`
12 | - the `type is not currently expressible` in PHP's type system, and so no type could be specified.
13 | - for return types, it is `not clear if the function will or will not return a value, other than null`.
14 |
15 | Reason for mixed types: A mixed type would allow people to add types to parameters, class properties and the function returns to indicate that the type information wasn't forgotten about, it just can't be specified more precisely, or the programmer explicitly decided not to do so.
16 |
17 | Because of the reasons above, it's a good thing the mixed type is added. Mixed itself means one of these types:
18 |
19 | - array
20 | - bool
21 | - callable
22 | - int
23 | - float
24 | - null
25 | - object
26 | - resource
27 | - string
28 |
29 | Example
30 |
31 | ```php
32 | function someMethod(): mixed
33 | ```
34 |
35 | Also note that since mixed already includes null, it's not allowed to make it nullable. The following will trigger an error:
36 |
37 | ```php
38 | function someMethod(): ?mixed
39 | ```
40 |
--------------------------------------------------------------------------------
/examples/NamedArguments/NamedArguments2.php:
--------------------------------------------------------------------------------
1 | printUserInfo(
48 | name: 'Kapil',
49 | age: 38,
50 | occupation: 'Developer',
51 | company: 'Cactus Global'
52 | );
53 |
54 | $instance->printUserInfo(
55 | school: 'Some School',
56 | occupation: 'Student',
57 | age: 8,
58 | name: 'Pari'
59 | );
60 |
--------------------------------------------------------------------------------
/examples/NullsafeOperator/NoNullsafePhp7.php:
--------------------------------------------------------------------------------
1 | country = 'India';
26 | }
27 | }
28 |
29 | class User
30 | {
31 | protected $address;
32 |
33 | public function __construct()
34 | {
35 | // Adding dummy address for demonstration purpose
36 | $this->address = new Address();
37 | }
38 |
39 | public function getAddress()
40 | {
41 | return $this->address;
42 | }
43 | }
44 |
45 | class Session
46 | {
47 | public $user;
48 |
49 | public function __construct()
50 | {
51 | // Adding dummy user for demonstration purpose
52 | $this->user = new User();
53 | }
54 | }
55 |
56 | $session = new Session();
57 |
58 | $country = null;
59 |
60 | if ($session !== null) {
61 | $user = $session->user;
62 |
63 | if ($user !== null) {
64 | $address = $user->getAddress();
65 |
66 | if ($address !== null) {
67 | $country = $address->country;
68 | }
69 | }
70 | }
71 |
72 | echo "Country is " . $country . PHP_EOL;
73 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # What's new in PHP 8
2 |
3 | This book is just the notes made by the author (Kapil Sharma) while going through new features of PHP 8. I'll be sharing this as ebook, just in case someone find these notes helpful.
4 |
5 | This ebook is supposed to be distributed during my next meetup where I'll be speaking on the topic `What's new in PHP 8`.
6 |
7 | For more details, either [download the ebook](https://github.com/kapilsharma/WhatsNewInPHP8/raw/master/book/whats-new-in-php8.pdf) or check [introduction](content/001-introduction.md) section of the book.
8 |
9 | ## License
10 |
11 | You can use these notes/ebook as you wish for personal learning. However, if you wish them to improve and share, please consider sending pull request of improvement and keep the original author's name. In short, you can use this book in anyway you want under [Creative Commons Attribution Share Alike 4.0 International](https://creativecommons.org/licenses/by-sa/4.0/) license.
12 |
13 | In short, the license says:
14 |
15 | - You are free to:
16 | - **Share** — copy and redistribute the material in any medium or format
17 | - **Adapt** — remix, transform, and build upon the material for any purpose, even commercially.
18 | - The licensor cannot revoke these freedoms as long as you follow the license terms.
19 | - Under the following terms:
20 | - **Attribution** — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
21 | - **ShareAlike** — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.
22 | - **No additional restrictions** — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.
23 |
--------------------------------------------------------------------------------
/content/105-constructor-promotion.md:
--------------------------------------------------------------------------------
1 | ## Constructor property promotion
2 |
3 | > RFC: [https://wiki.php.net/rfc/constructor_promotion](https://wiki.php.net/rfc/constructor_promotion)
4 |
5 | We have a similar thing in JavaScript, which allow writing less code, achieving the same result.
6 |
7 | ### Problem
8 |
9 | (Taken from RFC) Currently, the definition of simple value objects requires a lot of boilerplate, because all properties need to be repeated at least four times. Consider the following simple class:
10 |
11 | ```php
12 | class Point {
13 | public float $x;
14 | public float $y;
15 | public float $z;
16 |
17 | public function __construct(
18 | float $x = 0.0,
19 | float $y = 0.0,
20 | float $z = 0.0,
21 | ) {
22 | $this->x = $x;
23 | $this->y = $y;
24 | $this->z = $z;
25 | }
26 | }
27 | ```
28 |
29 | The properties are repeated:
30 |
31 | 1. In the property declaration,
32 | 1. The constructor parameters, and
33 | 1. two times in the property assignment.
34 |
35 | Additionally, the property type also repeated twice.
36 |
37 | Especially for value objects, which commonly do not contain anything more than property declarations and a constructor, this results in a lot of boilerplate and makes changes more complicated and error-prone.
38 |
39 | ### Solution
40 |
41 | PHP 8 will now support `Constructor Property Promotion`. With this, the above code can be written as:
42 |
43 | **File: examples/ConstructorPromotion/Point.php**
44 |
45 | ```php
46 | class Point
47 | {
48 | public function __construct(
49 | public float $x = 0.0,
50 | protected float $y = 0.0,
51 | private float $z = 0.0,
52 | ) {}
53 |
54 | public function print()
55 | {
56 | echo "Point($this->x, $this->y, $this->z)" . PHP_EOL;
57 | }
58 | }
59 |
60 | $point = new Point(1, 2.2);
61 | $point->print();
62 | ```
63 |
64 | Pretty, simple and a lot of fewer lines to achieve the same result, right?
65 |
66 | The output of the above program is
67 |
68 | ```bash
69 | Point(1, 2.2, 0)
70 | ```
71 |
72 | In simple words, if we define access modifier (public, protected, private) in the class constructor, that variable will behave as an instance variable and we need not declare it separately as an instance variable.
73 |
--------------------------------------------------------------------------------
/content/103-named-arguments.md:
--------------------------------------------------------------------------------
1 | ## Named arguments (also known as Named parameters)
2 |
3 | > RFC: [https://wiki.php.net/rfc/named_params](https://wiki.php.net/rfc/named_params)
4 |
5 | (Taken from RFC) Named arguments allow passing arguments to a function based on the parameter name, rather than the parameter position. It makes the meaning of the argument self-documenting, make them order-independent, and allows skipping default values arbitrarily.
6 |
7 | Let's check it with an example:
8 |
9 | **File: examples/NamedArguments/NamedArguments.php**
10 |
11 | ```php
12 | printUserInfo(
27 | name: 'Kapil',
28 | age: 38,
29 | occupation: 'Developer'
30 | );
31 |
32 | $instance->printUserInfo(
33 | occupation: 'Student',
34 | age: 8,
35 | name: 'Pari'
36 | );
37 | ```
38 |
39 | As seen in examples, with named parameters, we have given a name of each parameter and the order of parameters is no longer important. It also helps better-documented parameters right in the method signature.
40 |
41 | It will also be helpful in case when we have one or more optional arguments. Let's update our example with some optional parameters:
42 |
43 | **File: examples/NamedArguments/NamedArguments.php2**
44 |
45 | ```php
46 | class NamedArguments2
47 | {
48 | public function printUserInfo(
49 | string $name,
50 | int $age,
51 | string $occupation = '',
52 | string $company = '',
53 | string $school = ''
54 | ) {
55 | $output = "Hello $name, you are $age years old,";
56 |
57 | if ($occupation !== '') {
58 | $output .= " you are a $occupation";
59 | }
60 |
61 | if ($school !== '') {
62 | $output .= " and study in $school";
63 | }
64 |
65 | if ($company !== '') {
66 | $output .= " and work in $company";
67 | }
68 |
69 | echo $output . PHP_EOL;
70 | }
71 | }
72 |
73 | $instance = new NamedArguments2();
74 |
75 | $instance->printUserInfo(
76 | name: 'Kapil',
77 | age: 38,
78 | occupation: 'Developer',
79 | company: 'Cactus Global'
80 | );
81 |
82 | $instance->printUserInfo(
83 | school: 'Some School',
84 | occupation: 'Student',
85 | age: 8,
86 | name: 'Pari'
87 | );
88 | ```
89 |
90 | As we can see in two function call, we can not only move the order of parameters, we also no longer need to provide the first optional argument if we need to give the second optional argument. If we have to call the same function in PHP 7, we would have to do
91 |
92 | ```php
93 | $instance->printUserInfo('Kapil', 38, 'Developer', 'Cactus Global');
94 | $instance->printUserInfo('Pari', 8, 'Student', '', 'Some School');
95 | ```
96 |
97 | In the second call, even though we do not need company, we still have to pass an empty string in PHP 7 and earlier version.
98 |
--------------------------------------------------------------------------------
/content/106-static-return.md:
--------------------------------------------------------------------------------
1 | ## Static return type
2 |
3 | > RFC: [https://wiki.php.net/rfc/static_return_type](https://wiki.php.net/rfc/static_return_type)
4 |
5 | Before we can understand `static return type`, we must first understand `Late static binding`, introduced in PHP 5.2. You are already aware of that, skip next section and go to `Static return type`.
6 |
7 | ### Late static binding
8 |
9 | In PHP, if we want to return an instance of the same class, you must have used/seen `return new self` or `return new static`. What is the difference? Let's see an example to understand the difference:
10 |
11 | **File: examples/StaticReturn/LateStaticBinding.php**
12 |
13 | ```php
14 | class LateStaticBinding
15 | {
16 | public static function getSelf()
17 | {
18 | return new self();
19 | }
20 |
21 | public static function getStatic()
22 | {
23 | return new static();
24 | }
25 | }
26 |
27 | class LateStaticBindingSubClass extends LateStaticBinding
28 | {
29 |
30 | }
31 |
32 | echo get_class(LateStaticBindingSubClass::getSelf()) . PHP_EOL;
33 | echo get_class(LateStaticBindingSubClass::getStatic()) . PHP_EOL;
34 | echo get_class(LateStaticBinding::getSelf()) . PHP_EOL;
35 | echo get_class(LateStaticBinding::getStatic()) . PHP_EOL;
36 | ```
37 |
38 | Code is pretty simple; we defined a class `LateStaticBinding` which return its instance using `self` and `static`. Another class `LateStaticBindingSubClass` extends `LateStaticBinding` class, thus have access to parent class' methods.
39 |
40 | Next, we are calling these methods in the instance of two classes. Output is:
41 |
42 | ```bash
43 | LateStaticBinding
44 | LateStaticBindingSubClass
45 | LateStaticBinding
46 | LateStaticBinding
47 | ```
48 |
49 | Got the difference? When we use `self`, it always represents the class it is defined in, `LateStaticBinding` in our example. On the other hand, `static` represents the class from where it is called. Thus, it gives different results, depending on which class' instance it is called.
50 |
51 | It is called `late static binding` because it waits until the call and creates/return an instance of the class from where it is called.
52 |
53 | ### Static return type
54 |
55 | In later versions (after PHP 5.2), PHP allowed to have type hinting for the return type, but static could not be used there. This will now be possible in PHP 8. Following code demonstrate that, which will work only in PHP 8.
56 |
57 | **File: examples/StaticReturn/StaticReturnType.php**
58 |
59 | ```php
60 | class LateStaticBinding
61 | {
62 | public static function getSelf(): self
63 | {
64 | return new self();
65 | }
66 |
67 | public static function getStatic(): static
68 | {
69 | return new static();
70 | }
71 | }
72 |
73 | class LateStaticBindingSubClass extends LateStaticBinding
74 | {
75 |
76 | }
77 |
78 | echo get_class(LateStaticBindingSubClass::getSelf()) . PHP_EOL;
79 | echo get_class(LateStaticBindingSubClass::getStatic()) . PHP_EOL;
80 | echo get_class(LateStaticBinding::getSelf()) . PHP_EOL;
81 | echo get_class(LateStaticBinding::getStatic()) . PHP_EOL;
82 | ```
83 |
84 | Please note, we defined static return type in `public static function getStatic(): static`
85 |
--------------------------------------------------------------------------------
/content/109-throw-expression.md:
--------------------------------------------------------------------------------
1 | ## Throw expression
2 |
3 | > RFC: [https://wiki.php.net/rfc/throw_expression](https://wiki.php.net/rfc/throw_expression)
4 |
5 | Throw in PHP 7 and earlier version was a statement.
6 |
7 | ### Statement vs expression
8 |
9 | - Stagement: A line of code which does something like `for`, `if`, etc.
10 | - Expression: A line of code which evalute something like `1 + 2`, `$a == $b` (evalute true of false), etc.
11 |
12 | ### Throw statement
13 |
14 | In PHP version 7 and earlier, the throw was a statement, that means, it cannot be used at places where expression is needed. Examples (Taken from RFC)
15 |
16 | ```php
17 | // This was previously not possible since arrow functions only accept a single expression while throw was a statement.
18 | $callable = fn() => throw new Exception();
19 |
20 | // $value is non-nullable.
21 | $value = $nullableValue ?? throw new InvalidArgumentException();
22 |
23 | // $value is truthy.
24 | $value = $falsableValue ?: throw new InvalidArgumentException();
25 |
26 | // $value is only set if the array is not empty.
27 | $value = !empty($array)
28 | ? reset($array)
29 | : throw new InvalidArgumentException();
30 | ```
31 |
32 | Above lines of code will generate errors in PHP 7 and earlier.
33 |
34 | PHP 8 made `throw statement` to `throw expression` and above code will work.
35 |
36 | ### Operator precedence
37 |
38 | Now since throw is an expression (like operators), its position in operator precedence is important. Below examples (Taken from RFC) will explain operator precedence of throw expression.
39 |
40 | ```php
41 | throw $this->createNotFoundException();
42 | // Evaluated as
43 | throw ($this->createNotFoundException());
44 | // Instead of
45 | (throw $this)->createNotFoundException();
46 |
47 | throw static::createNotFoundException();
48 | // Evaluated as
49 | throw (static::createNotFoundException());
50 | // Instead of
51 | (throw static)::createNotFoundException();
52 |
53 | throw $userIsAuthorized ? new ForbiddenException() : new UnauthorizedException();
54 | // Evaluated as
55 | throw ($userIsAuthorized ? new ForbiddenException() : new UnauthorizedException());
56 | // Instead of
57 | (throw $userIsAuthorized) ? new ForbiddenException() : new UnauthorizedException();
58 |
59 | throw $maybeNullException ?? new Exception();
60 | // Evaluated as
61 | throw ($maybeNullException ?? new Exception());
62 | // Instead of
63 | (throw $maybeNullException) ?? new Exception();
64 |
65 | throw $exception = new Exception();
66 | // Evaluated as
67 | throw ($exception = new Exception());
68 | // Instead of
69 | (throw $exception) = new Exception();
70 |
71 | throw $exception ??= new Exception();
72 | // Evaluated as
73 | throw ($exception ??= new Exception());
74 | // Instead of
75 | (throw $exception) ??= new Exception();
76 |
77 | throw $condition1 && $condition2 ? new Exception1() : new Exception2();
78 | // Evaluated as
79 | throw ($condition1 && $condition2 ? new Exception1() : new Exception2());
80 | // Instead of
81 | (throw $condition1) && $condition2 ? new Exception1() : new Exception2();
82 | ```
83 |
84 | **Important:** Keeping throw at lower precedence will have a side effect, `throw between two short-circuit operators would not be possible without parentheses`. Example:
85 |
86 | ```php
87 | $condition || throw new Exception('$condition must be truthy')
88 | && $condition2 || throw new Exception('$condition2 must be truthy');
89 | // Evaluated as
90 | $condition || (throw new Exception('$condition must be truthy') && $condition2 || (throw new Exception('$condition2 must be truthy')));
91 | // Instead of
92 | $condition || (throw new Exception('$condition must be truthy'))
93 | && $condition2 || (throw new Exception('$condition2 must be truthy'));
94 | ```
95 |
96 | Although it will be a rare case, if we decide to use this, we must use proper parentheses.
97 |
--------------------------------------------------------------------------------
/assets/theme-light.html:
--------------------------------------------------------------------------------
1 |
166 |
167 |
171 |
--------------------------------------------------------------------------------
/examples/changelog.md:
--------------------------------------------------------------------------------
1 | # Change log
2 |
3 | This file contains the log of what was changed in which version
4 |
5 | # version1.0.0
6 |
7 | This is the version of ebook, to be distributed during Laravel-Nagpur meetup on November 27th, 2020. `[*]` represent done and `[ ]` represent planned but not completed. Following things were added to the book version 1.0.0.
8 |
9 | - 001. Introduction
10 | - 100. Major new changes
11 | - [*] 101 Union Types
12 | - [*] 102 The nullsafe operator
13 | - [*] 103 Named arguments
14 | - [*] 104 Attributes
15 | - [*] 105 Class constructor property promotion
16 | - [*] 106 Static return
17 | - [*] 107 Match Expressions
18 | - [*] 108 Mixed types
19 | - [*] 109 throw expression
20 | - [ ] 110 Weak maps
21 | - [*] 111 Stringable interface
22 | - [*] 112 Validation for abstract trait methods
23 | - 200 New functions
24 | - [*] 201 str_contains
25 | - [*] 202 str_starts_with & str_ends_with
26 | - [*] 203 get_debug_type
27 | - [*] 205 fdiv
28 | - 300 Syntax changes
29 | - [*] 301 object::class
30 | - [*] 302 Non-capturing catches
31 | - [*] 303 Allow trailing comma in parameter list
32 | - 400 Breaking changes
33 | - 500 Language internal improvement
34 | - JIT
35 | - Inheritance with private methods
36 |
37 | ### New functions
38 | - New `str_contains` function
39 | - New `fdiv` function
40 | - New `get_debug_type` function
41 | - New `preg_last_error_msg` function
42 | - `::class` magic constant is now supported on objects
43 | - New `ValueError` Error Exception
44 | - New `PhpToken` Tokenizer class
45 | - New `str_starts_with` and `str_ends_with` functions
46 | - New `mixed` pseudo type
47 | - New `get_resource_id` function
48 | - New `DateTime/DateTimeImmutable::createFromInterface()` methods
49 | - New `p` date format for UTC `Z` time zone designation
50 | - Built-in web server supports dynamic port selection
51 | - New `Stringable` interface
52 | - New `%h` and `%H` `printf` specifiers
53 | - New `*` precision and width modifiers in `printf`
54 | - Stack trace as string - Parameter max length is configurable
55 |
56 | ### Syntax/Functionality Changes
57 |
58 | - Internal function warnings now throw `TypeError` and `ValueError` exceptions
59 | - Expressions can now `throw` Exceptions
60 | - JSON extension is always available
61 | - `catch` exceptions only by type
62 | - `+`/`-` operators take higher precedence when used with concat (`.`) operator
63 | - `CurlHandle` class objects replace curl handlers
64 | - Fatal errors on incompatible method signatures
65 | - Disabled functions behave as if they do not exist
66 | - `GdImage` class objects replace GD image resources
67 | - Assertions throw exceptions by default
68 | - Sockets extension resources (`Socket` and `AddressInfo`) are class objects
69 | - `crypt()` function requires `$salt` parameter
70 | - GD Extension: Windows DLL file name changed from `php_gd2.dll` to `php_gd.dll`
71 | - PHP Startup Errors are displayed by default
72 | - `substr`, `iconv_substr`, `grapheme_substr` return empty string on out-of-bound offsets
73 | - Class magic method signatures are strictly enforced
74 | - Locale-independent `float` to `string` casting
75 | - Calling non-static class methods statically result in a fatal error
76 | - Inheritance rules are not applied to `private` class methods
77 | - Default error reporting is set to `E_ALL`
78 | - Apache Handler: Module name and file path changes
79 | - PDO: Default error mode set to exceptions
80 | - `@` Error Suppression operator does not silent fatal errors
81 |
82 | ### Deprecations
83 |
84 | - Deprecate required parameters after optional parameters in function/method signatures
85 | - `ReflectionParameter::getClass())`, `::isArray()`, and `::isCallable()` methods deprecated
86 | - Disabled functions: Reflection and `get_defined_functions()` deprecations
87 | - `libxml_disable_entity_loader` function is deprecated
88 | - PostgreSQL: Several aliased functions are deprecated
89 | - Removed Features and Functionality
90 | - XMLRPC extension is moved to PECL
91 | - `FILTER_FLAG_SCHEME_REQUIRED` and `FILTER_FLAG_HOST_REQUIRED` flags are removed
92 |
93 | ### Revisit
94 |
95 | - Create DateTime objects from interface
96 | - Object implementation of token_get_all()
97 | - Variable syntax tweaks
98 |
--------------------------------------------------------------------------------
/content/107-match.md:
--------------------------------------------------------------------------------
1 | ## Match expression
2 |
3 | **RFC: [https://wiki.php.net/rfc/match_expression_v2](https://wiki.php.net/rfc/match_expression_v2)**
4 |
5 | PHP 8 is trying to reduce the line of code and match expression do so in switch cases. Consider this simple switch case example:
6 |
7 | **File: examples/Match/Switch.php**
8 |
9 | ```php
10 | function printFavoriteColor($colour)
11 | {
12 | switch ($colour) {
13 | case 'red':
14 | echo 'Your favorite colour is Red.' . PHP_EOL;
15 | case 'blue':
16 | echo 'Your favorite colour is Blue.' . PHP_EOL;
17 | case 'green':
18 | echo 'Your favorite colour is Green.' . PHP_EOL;
19 | default:
20 | echo 'You do not like any primary colour.' . PHP_EOL;
21 | }
22 | }
23 |
24 | printFavoriteColor('Yellow');
25 | ```
26 |
27 | A simple program and as you must have correctly expected, the output is `You do not like any primary colour.`
28 |
29 | PHP 8 introduce a new `match` expression, which can achieve the same result as above. Let's see that:
30 |
31 | **File: examples/Match/Match.php**
32 |
33 | ```php
34 | function printFavoriteColor($colour)
35 | {
36 | echo match ($colour) {
37 | 'red' => 'Your favorite colour is Red.' . PHP_EOL,
38 | 'blue' => 'Your favorite colour is Blue.' . PHP_EOL,
39 | 'green' => 'Your favorite colour is Green.' . PHP_EOL,
40 | default => 'You do not like any primary colour.' . PHP_EOL,
41 | };
42 | }
43 |
44 | printFavoriteColor('Yellow');
45 | ```
46 |
47 | Shorter code but it will have the same result.
48 |
49 | ### Strict type comparison
50 |
51 | Switch loosely compare cases with `==`. Let's see an example.
52 |
53 | **File: examples/Match/Switch2.php** (Example taken from RFC & slightly fixed)
54 |
55 | ```php
56 | switch (false) {
57 | case 0:
58 | $result = "Oh no!\n";
59 | break;
60 | case false:
61 | $result = "This is what I expected\n";
62 | break;
63 | }
64 | echo $result;
65 | ```
66 |
67 | At a first look, we might be expecting the result to be `This is what I expected` but we get the result as `Oh no!`. Reason is, `0 == false` returns true. Let's see the same example with the match, which does strict comparison `===`.
68 |
69 | **File: examples/Match/Match2.php**
70 |
71 | ```php
72 | echo match (false) {
73 | 0 => "Oh no!\n",
74 | false => "This is what I expected\n",
75 | };
76 | ```
77 |
78 | It will return the expected output `This is what I expected`.
79 |
80 | ### No break needed
81 |
82 | In switch, we need to `break` each case to stop further execution of switch. Let's check an example:
83 |
84 | **File: examples/Match/Switch3.php**
85 |
86 | ```php
87 | switch (1) {
88 | case 1:
89 | case 2:
90 | echo 'Number 1 or 2.' . PHP_EOL;
91 | case 3:
92 | case 4:
93 | echo 'Number 3 or 4.' . PHP_EOL;
94 | }
95 | ```
96 |
97 | What output are we expecting? Isn't it `Number 1 or 2.` but the output is
98 |
99 | ```bash
100 | Number 1 or 2.
101 | Number 3 or 4.
102 | ```
103 |
104 | It's obvious as we forget to `break` before `case 3`. To fix it, we need to add break
105 |
106 | **File: examples/Match/Switch4.php**
107 |
108 | ```php
109 | switch (1) {
110 | case 1:
111 | case 2:
112 | echo 'Number 1 or 2.' . PHP_EOL;
113 | break; // Added this line
114 | case 3:
115 | case 4:
116 | echo 'Number 3 or 4.' . PHP_EOL;
117 | }
118 | ```
119 |
120 | Same example with match do not need break
121 |
122 | **File: examples/Match/Match3.php**
123 |
124 | ```php
125 | echo match (1) {
126 | 1, 2 => 'Number for 1 and 2' . PHP_EOL,
127 | 3, 4 => 'Number for 3 and 4' . PHP_EOL,
128 | };
129 | ```
130 |
131 | ### Return value
132 |
133 | In the above example, you may also have noticed that we need to do `echo` in each switch case. However, in case of a match, we echo only once before `match`.
134 |
135 | This is due to the fact that switch simply executes the statements but match return the result of statements. This will be especially helpful when we need to return value from `match` in a function. With a switch, we need multiple returns or make a local variable and return the variable after the switch. With the match, we can simply do `return match(){}`.
136 |
--------------------------------------------------------------------------------
/content/104-attributes.md:
--------------------------------------------------------------------------------
1 | ## Attributes
2 |
3 | > RFC: [https://wiki.php.net/rfc/attributes_v2](https://wiki.php.net/rfc/attributes_v2)
4 |
5 | What is metadata?
6 |
7 | In programming, metadata is a `data that defines another data`, on in other words `data about data`. Confusing, let's see an example:
8 |
9 | ```PHP
10 | /**
11 | * Returns user model by user id
12 | *
13 | * @param int $userId
14 | * @return User User Model fetched from user id.
15 | */
16 | public function getUser($userId): User
17 | {
18 | // Code to fetch and return user object.
19 | }
20 | ```
21 |
22 | You must have seen similar code, where we define some documentation in Docblock comment. Here, two important things are, `@param` and `@return`, what they are? It does not contribute anything to the code, but a document generator use them to generate documentation and IDE like PHP Storm will read this docblock to show a warning at the time of development. For example, if you write `$userService->getUser('Kapil')`, your IDE will show an error that `function expects int but we are passing a string`. This will happen even though we did not type-hint in method parameters but because IDE will do it by reading `@param`, where we defined `$userId` must be an integer.
23 |
24 | Thus, these docblocks may define what our data is (data about the data) and known as metadata.
25 |
26 | However, for PHP interpreter, this whole docblock is just a comment and will be ignored. In other words, there is no inbuilt support in PHP to define metadata. It will now change in PHP 8 with Attributes.
27 |
28 | Attributes are also known as `annotations` in a few other languages. The attributes, in PHP 8 are supposed to add metadata to classes, methods, properties/variables, method parameters, etc. Let's check an example of where these properties can be used (Taken from RFC)
29 |
30 | ```php
31 | #[ExampleAttribute]
32 | class Foo
33 | {
34 | #[ExampleAttribute]
35 | public const FOO = 'foo';
36 |
37 | #[ExampleAttribute]
38 | public $x;
39 |
40 | #[ExampleAttribute]
41 | public function foo(#[ExampleAttribute] $bar) { }
42 | }
43 |
44 | $object = new #[ExampleAttribute] class () { };
45 |
46 | #[ExampleAttribute]
47 | function f1() { }
48 |
49 | $f2 = #[ExampleAttribute] function () { };
50 |
51 | $f3 = #[ExampleAttribute] fn () => 1;
52 | ```
53 |
54 | ### Practical use of attributes
55 |
56 | Let's see a test code to see the practical use of Attributes. We first need to define an attribute.
57 |
58 | **File: examples/Attributes/TestAttribute.php**
59 |
60 | ```php
61 | #[\Attribute]
62 | class TestAttribute
63 | {
64 | public string $testArgument;
65 |
66 | public function __construct(string $testArgument)
67 | {
68 | $this->testArgument = $testArgument;
69 | }
70 | }
71 | ```
72 |
73 | Here, we made a new `TestAttribute` class. It's a `custom attribute` as we defined it for our project/example. To define a custom attribute, we use a `global attribute`, which is `#[\Attribute]`. Custome attribute in our example is nothing but just a PHP class, attributed by `#[\Attribute]` and can store some extra data (`$testArgument`).
74 |
75 | Now we defined a custom attribute, let's use it in a class.
76 |
77 | **File: examples/Attributes/TestClass.php**
78 |
79 | ```php
80 | #[TestAttribute('Some metadata for TestClass')]
81 | class TestClass
82 | {
83 |
84 | }
85 | ```
86 |
87 | Here we defined a TestClass and added our custom attribute (TestAttribute) with some more metadata (Some metadata for TestClass).
88 |
89 | Now we are set to fetch this metadata through code.
90 |
91 | **File: examples/Attributes/testing_attributes.php**
92 |
93 | ```php
94 | $reflection = new \ReflectionClass(TestClass::class);
95 | $classAttributes = $reflection->getAttributes();
96 |
97 | echo $classAttributes[0]->newInstance()->testArgument . PHP_EOL;
98 | ```
99 |
100 | Its output will be `Some metadata for TestClass`, which is our actual metadata. In our code, first line creates a `ReflectionClass` of `TestClass`. Later, we get the attributes of the class (TestClass) but `getAttributes` method, which will be an array. Since we have only one attribute, we are using the first array element, creating an instance of the class and fetching `testArgument`, which we defined while making the class.
101 |
102 | ### How it will impact me as a developer?
103 |
104 | Well, for the majority of developers, it will not, at least not directly and immediately.
105 |
106 | It will be used by IDE to fetch metadata in a standard way provided we define it. Also, after some time, newer versions of frameworks like Laravel, Symfony, Zend Framework, Yii, Cake PHP, Drupal, Magento etc will be using attributes to define a lot of things. Symfony already declared in [a blog post(https://symfony.com/blog/new-in-symfony-5-2-php-8-attributes)](https://symfony.com/blog/new-in-symfony-5-2-php-8-attributes) that starting version 5.2, they will be using attributes to define routes.
107 |
108 | It is good to know about them so that they do not seem alien when first introduced in your favourite framework.
109 |
--------------------------------------------------------------------------------
/content/102-nullsafe-operator.md:
--------------------------------------------------------------------------------
1 | ## Nullsafe operator
2 |
3 | > RFC: [https://wiki.php.net/rfc/nullsafe_operator](https://wiki.php.net/rfc/nullsafe_operator)
4 |
5 | ### History
6 |
7 | Skip to title `Changes in PHP 8` if you are interested to see only the changes made in PHP 8.
8 |
9 | ### PHP ternary operator
10 |
11 | The ternary operator is available in PHP since very long thus hopefully, you already know about it. Its syntax is `(expr1) ? (expr2) : (expr3)`, it is equivalent to
12 |
13 | ```php
14 | if ($expr1) {
15 | return $expr2;
16 | } else {
17 | return $expr3;
18 | }
19 | ```
20 |
21 | ### Ternary shortcut (?:)
22 |
23 | The shortcut operator does not have `expr2` of the above example. It is like `(expr1) ?: (expr2)`. Let's understand it by an example:
24 |
25 | ```php
26 | $result = $variable1 ?: $variable2;
27 | ```
28 |
29 | Which is equivalent to
30 |
31 | ```php
32 | $result = $variable1 ? $variable1 : $variable2;
33 | ```
34 |
35 | or
36 |
37 | ```php
38 | if ($variable1) {
39 | $result = $variable1;
40 | } else {
41 | $result = $variable2;
42 | }
43 | ```
44 |
45 | ### Null coalescing operator (??)
46 |
47 | PHP 7.0 introduced `Null coalescing operator (??)`, which confuse some people between `??` and `?:` operators. Following example is taken from [PHP manual (https://www.php.net/manual/en/migration70.new-features.php)](https://www.php.net/manual/en/migration70.new-features.php):
48 |
49 | `The null coalescing operator (??) has been added as syntactic sugar for the common case of needing to use a ternary in conjunction with isset(). It returns its first operand if it exists and is not NULL; otherwise, it returns its second operand.`
50 |
51 | ```php
52 |
64 | ```
65 |
66 | To make it more clear, let's see a few more examples with a comparison between `??` and `?:`:
67 |
68 | ```php
69 | // Lets compare them on null variable.
70 | $a = null;
71 | print $a ?: 'b'; // Output: b, because if(null) returns false
72 | print $a ?? 'b'; // Output: b, because isset(null) returns false
73 |
74 | // Let's check a variable which is not defined
75 | print $c ?: 'a'; // Output: Notice: Undefined variable: c
76 | print $c ?? 'a'; // Output: a, because isset($c) is false.
77 |
78 | // Let's check it once again with an array key
79 | $b = array('a' => null);
80 | print $b['a'] ?? 'd'; // Output: d, because $b['a'] is null, which is not true.
81 | print $b['a'] ?: 'd'; // Output: d, because isset($b['a']) = isset(null) = false
82 | ```
83 |
84 | Thus, ternary shortcut (?:) check the variable for truth (`if ($variable)`) and null coalescing operator check the variable with `isset()` method. You may check much better comparison between these two conditions on [PHP Manual (https://www.php.net/manual/en/types.comparisons.php)](https://www.php.net/manual/en/types.comparisons.php)
85 |
86 | ### Changes in PHP 8: nullsafe operator (?->)
87 |
88 | Problem with the null coalescing operator is, it works only with variables. Thus, PHP 8 introduced nullsafe operator (?->)[(Check RFC: https://wiki.php.net/rfc/nullsafe_operator)](https://wiki.php.net/rfc/nullsafe_operator) to solve this problem.
89 |
90 | Let's understand it with an example:
91 |
92 | **examples/NullsafeOperator/NoNullsafePhp7.php**
93 |
94 | ```php
95 | class Address
96 | {
97 | public $country;
98 |
99 | public function __construct()
100 | {
101 | // Adding dummy country for demonstration purpose
102 | $this->country = 'India';
103 | }
104 | }
105 |
106 | class User
107 | {
108 | protected $address;
109 |
110 | public function __construct()
111 | {
112 | // Adding dummy address for demonstration purpose
113 | $this->address = new Address();
114 | }
115 |
116 | public function getAddress()
117 | {
118 | return $this->address;
119 | }
120 | }
121 |
122 | class Session
123 | {
124 | public $user;
125 |
126 | public function __construct()
127 | {
128 | // Adding dummy user for demonstration purpose
129 | $this->user = new User();
130 | }
131 | }
132 |
133 | $session = new Session();
134 |
135 | $country = null;
136 |
137 | if ($session !== null) {
138 | $user = $session->user;
139 |
140 | if ($user !== null) {
141 | $address = $user->getAddress();
142 |
143 | if ($address !== null) {
144 | $country = $address->country;
145 | }
146 | }
147 | }
148 |
149 | echo "Country is " . $country . PHP_EOL;
150 | ```
151 |
152 | I've taken an example of original RFC and extended it to be executable on the command line. Although code is simple and self-explaining, let me explain it. Here, I try to demonstrate a web session, session contains a user, who has an address containing the user's country. As we need to validate if the required information is present, we need to have three if conditions:
153 |
154 | ```php
155 | if ($session !== null) {
156 | $user = $session->user;
157 |
158 | if ($user !== null) {
159 | $address = $user->getAddress();
160 |
161 | if ($address !== null) {
162 | $country = $address->country;
163 | }
164 | }
165 | }
166 | ```
167 |
168 | With nullsafe operator, this whole code can be written in one line as `$session?->user?->getAddress()?->country`. Complete code will be:
169 |
170 | **File: examples/NullsafeOperator/Nullsafe.php**
171 |
172 | ```php
173 | class Address
174 | {
175 | public $country;
176 |
177 | public function __construct()
178 | {
179 | // Adding dummy country for demonstration purpose
180 | $this->country = 'India';
181 | }
182 | }
183 |
184 | class User
185 | {
186 | protected $address;
187 |
188 | public function __construct()
189 | {
190 | // Adding dummy address for demonstration purpose
191 | $this->address = new Address();
192 | }
193 |
194 | public function getAddress()
195 | {
196 | return $this->address;
197 | }
198 | }
199 |
200 | class Session
201 | {
202 | public $user;
203 |
204 | public function __construct()
205 | {
206 | // Adding dummy user for demonstration purpose
207 | $this->user = new User();
208 | }
209 | }
210 |
211 | $session = new Session();
212 |
213 | $country = $session?->user?->getAddress()?->country;
214 |
215 | echo "Country is " . $country . PHP_EOL;
216 | ```
217 |
--------------------------------------------------------------------------------
/content/001-introduction.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 |
3 | **Book version 1.0.0**
4 |
5 | This book is just the notes made by the author (Kapil Sharma) while going through new features of PHP 8. I'll be sharing this as an ebook, in case someone finds these notes helpful.
6 |
7 | I'll be first sharing this book during Laravel-Nagpur meetup on November 27th, 2020 at 7:30 pm IST, presented by me and live-streamed on YouTube. If you are reading the book later, you may see the recording on that meetup on Laravel-Nagpur's [YouTube channel (https://bit.ly/laravel-ngp-nov-2020)](https://bit.ly/laravel-ngp-nov-2020)
8 |
9 | PHP 8 is the next major version of PHP. As like in any major version, there will be some breaking changes. That means we can not update PHP version on the server and expect everything to be working.
10 |
11 | We are dividing this ebook into few sections:
12 |
13 | - Major new changes.
14 | - New functions.
15 | - Syntax changes
16 | - Breaking changes
17 | - Internal improvements
18 |
19 | ### Code examples.
20 |
21 | During the meet-up, we will be using a practical approach, lot of examples will be shown during the meet-up and also used in this book. You can get all of these examples by cloning [Git repository (https://github.com/kapilsharma/WhatsNewInPHP8)](https://github.com/kapilsharma/WhatsNewInPHP8).
22 |
23 | Once you clone the repository, it has the following folders:
24 |
25 | - `assets` - Ignore them. They are some assets used for making the ebook.
26 | - `contents` - You can ignore them as well. It contains markdown files used to generate ebook. You may better use PDF ebook. If you wish to make any correction in the generated PDF, you may consider to fix the error/typo in related markdown and probably send back pull request with the correction.
27 | - `example` - This folder contains the example PHP code used in the ebook. Each feature given in this book, have its folder under example and contains the code of examples given in the section. Even in the ebook, the file name of the example is there above the code.
28 | - `export` - You will not get this folder after clone. However, if you decide to generate the ebook, the folder will be created automatically. Thanks to [Mohamed Said](https://twitter.com/themsaid), I'm using [ibis](https://github.com/themsaid/ibis) to generate this ebook. If you wish to make some correction (please consider PR) and regenerate the ebook yourself, just setup ibis and run `ibis build` command. This folder is added to '.gitignore' file to avoid committing it.
29 |
30 | ### Running the examples in PHP 8
31 |
32 | At the time of writing this ebook, PHP 8 is still not released. I'm pretty sure it will be released by the time you will be reading this ebook. If not, or you may not install PHP 8 on your system for some reasons, you may use PHP 8 docker with following command (Expecting docker is installed on your system, if no, just DuckDuckGo as I can't cover docker installation in this book):
33 |
34 | ```bash
35 | docker run -it -v "$PWD":/usr/src/app -w /usr/src/app php:8.0.0RC5 /bin/bash
36 | ```
37 |
38 | Please note, `PHP 8.0.0 Release candidate 5` is latest at the time of writing this ebook. You may want to check PHP's official [dockerhub (https://hub.docker.com/_/php?tab=tags)](https://hub.docker.com/_/php?tab=tags) to check the latest PHP 8 version available and change `php:8.0.0RC5` in the above command with the latest version.
39 |
40 | Also, it is recommended to run `composer install` (locally, not in docker) to generate required autoloading, which is needed for few examples.
41 |
42 | ### License
43 |
44 | You can use these notes as you wish for personal learning. However, if you wish them to improve and share, please consider sending a pull request for improvement and keep the original author's name. In short, if you want to use these notes in any way, feel free to use it under [Creative Commons Attribution Share Alike 4.0 International](https://creativecommons.org/licenses/by-sa/4.0/) license.
45 |
46 | ### About author
47 |
48 | **Short intro:** I'm Kapil Sharma, developing web applications since 15+ years in different technologies including PHP. I started my career in 2004, as a web designer, working in HTML 4 (Without CSS, using huge tables with a lot of rowspan and colspan for designing web pages). Soon I started using CSS 2, JavaScript and PHP 4/5. Later I started working as a developer in PHP and Java/J2EE. By the end of 2005, I completely started working on Java/J2EE and worked on it for next 3 years, mostly on OFBiz, Mule, Service Mix (Java) and occasionally on Joomla version 1 (PHP). In 2009, I worked on PHP, Python and Ruby on Rails and since 2010, I started working mostly in PHP.
49 |
50 | I used PHP without using any framework, Code Igniter, Zend Framework 1, Symfony and nowadays mostly Laravel and Node JS along with Angular and Vue JS for frontend.
51 |
52 | I also help to manage PHP Reboot, a developers' community in Pune, India since 2014 and a regular speaker on PHP Reboot and other meetups.
53 |
54 | Right now, I'm working as Technical Architect at Cactus Global, Mumbai but I'm serving my notice period and soon will be joining Parker Consulting in Pune.
55 |
56 | ### Credits
57 |
58 | While learning new features of PHP 8, making my notes and converting them into this ebook, I went through a lot of websites, articles, tutorials, PHP manual and official RFC. I mostly used search engine (Google and DuckDuckGo) to find them. I did not remember the link of all those pages but I'm thankful to everyone who posted any article/tutorial about new features of PHP 8, they helped me learn new features of PHP 8, make my notes and this book.
59 |
60 | Most important were the PHP RFC, which I added as a source wherever possible. Until PHP 8 documentation is available, RFC is the best place to understand an upcoming feature.
61 |
62 | I'd like to specially mention [stitcher.io blogs](https://stitcher.io/blog/page-1), which help me to understand a few RFC in details.
63 |
64 | I used free service of [canva.com](https://www.canva.com) to design the cover page of the book and would like to thanks them for providing an online tool to create a free book cover.
65 |
66 | ## Issues
67 |
68 | If you find any issue in the book, feel free to send a PR or at least [create a bug report (issue at https://github.com/kapilsharma/WhatsNewInPHP8/issues)](https://github.com/kapilsharma/WhatsNewInPHP8/issues) on GitHub. I will frequently check the issue and fix the issues in next version of the book.
69 |
70 | ## Improvements, book version
71 |
72 | There is a chance of improvement in every work. I'll be doing it myself and fixing the issue raised by others. Make sure you have latest version of the book. Book version is given at the top of the introduction section of the book and on [GitHub (https://github.com/kapilsharma/whatsnewinphp8)](https://github.com/kapilsharma/whatsnewinphp8). If you find you have older version, please download the newer version of the book with bug fix and new features.
73 |
--------------------------------------------------------------------------------
/content/400-breaking-changes.md:
--------------------------------------------------------------------------------
1 | # Breaking changes
2 |
3 | Link any major version, PHP 8 also introduced some breaking changes. Thus, we need to look at them and fix our existing projects, before upgrading PHP to version 8 on the server.
4 |
5 | Once PHP 8 is released, documentation will cover PHP 7 to PHP 8 migration guide, with breaking changes. However, until documentation is available in PHP manual, we can see it at [https://github.com/php/php-src/blob/PHP-8.0/UPGRADING#L20](https://github.com/php/php-src/blob/PHP-8.0/UPGRADING#L20)
6 |
7 | ## Consistent type errors for internal functions
8 |
9 | > RFC: [https://wiki.php.net/rfc/consistent_type_errors](https://wiki.php.net/rfc/consistent_type_errors)
10 |
11 | For user-defined functions, passing a parameter of illegal type results in a TypeError. For internal functions, the behaviour depends on multiple factors, but the default is to throw a warning and return null.
12 |
13 | Example of user-defined function in PHP 7
14 |
15 | ```php
16 | function foo(int $bar) {}
17 | foo("not an int");
18 | // TypeError: Argument 1 passed to foo() must be of the type int, string given
19 | ```
20 |
21 | Example of internal function in PHP 7
22 |
23 | ```php
24 | var_dump(strlen(new stdClass));
25 | // Warning: strlen() expects parameter 1 to be string, object given
26 | // NULL
27 | ```
28 |
29 | However we can change this behaviour by using strict_types in PHP 7.
30 |
31 | ```php
32 | declare(strict_types=1);
33 | var_dump(strlen(new stdClass));
34 | // TypeError: strlen() expects parameter 1 to be string, object given
35 | ```
36 |
37 | **In PHP 8, internal parameter parsing APIs always generate a TypeError if parameter parsing fails.**
38 |
39 | ## Change Default PDO Error Mode
40 |
41 | > RFC: [https://wiki.php.net/rfc/pdo_default_errmode](https://wiki.php.net/rfc/pdo_default_errmode)
42 |
43 | The current default error mode for PDO is silent. This means that when an SQL error occurs, no errors or warnings may be emitted and no exceptions thrown unless the developer implements their own explicit error handling.
44 |
45 | This causes issues for new developers because the only errors they often see from PDO code are knock-on errors such as “call to fetch() on non-object” - there's no indication that the SQL query (or other action) failed or why.
46 |
47 | ## Change the precedence of the concatenation operator
48 |
49 | > RFC: [https://wiki.php.net/rfc/concatenation_precedence](https://wiki.php.net/rfc/concatenation_precedence)
50 |
51 | It's been a long standing issue that an (unparenthesized) expression with '+', '-' and '.' evaluates left-to-right.
52 |
53 | ```PHP
54 | echo "sum: " . $a + $b;
55 |
56 | // current behavior: evaluated left-to-right
57 | echo ("sum: " . $a) + $b;
58 |
59 | // desired behavior: addition and subtraction have a higher precendence
60 | echo "sum :" . ($a + $b);
61 | ```
62 |
63 | ## Stricter type checks for arithmetic/bitwise operators
64 |
65 | RFC: [https://wiki.php.net/rfc/arithmetic_operator_type_checks](https://wiki.php.net/rfc/arithmetic_operator_type_checks)
66 |
67 | This RFC proposes to throw a TypeError when arithmetic or bitwise operator is applied to an array, resource or (non-overloaded) object. The behaviour of scalar operands (like null) remains unchanged
68 |
69 | This is not reasonable behaviour:
70 |
71 | ```php
72 | var_dump([] % [42]);
73 | // int(0)
74 | ```
75 |
76 | ## Saner numeric strings
77 |
78 | > RFC: [https://wiki.php.net/rfc/saner-numeric-strings](https://wiki.php.net/rfc/saner-numeric-strings)
79 |
80 | The PHP language has a concept of numeric strings, strings which can be interpreted as numbers.
81 |
82 | A string can be categorised in three ways
83 |
84 | - A numeric string is a string containing only a number, optionally preceded by whitespace characters. For example, "123" or " 1.23e2".
85 | - A leading-numeric string is a string that begins with a numeric string but is followed by non-number characters (including whitespace characters). For example, "123abc" or "123 ".
86 | - A non-numeric string is a string which is neither a numeric string nor a leading-numeric string.
87 |
88 | PHP 8 will unify the various numeric string modes into a single concept: Numeric characters only with both leading and trailing whitespace allowed. Any other type of string is non-numeric and will throw TypeErrors when used in a numeric context.
89 |
90 | ## Saner string to number comparisons
91 |
92 | > RFC: [https://wiki.php.net/rfc/string_to_number_comparison](https://wiki.php.net/rfc/string_to_number_comparison)
93 |
94 | Comparisons between strings and numbers using == and other non-strict comparison operators currently work by casting the string to a number, and subsequently performing a comparison on integers or floats. This results in many surprising comparison results, the most notable of which is that 0 == "foobar" returns true.
95 |
96 | The single largest source of bugs is likely the fact that `0 == "foobar"` returns true. Quite often this is encountered in cases where the comparison is implicit, such as in_array() or switch statements. A classic example:
97 |
98 | ```php
99 | $validValues = ["foo", "bar", "baz"];
100 | $value = 0;
101 | var_dump(in_array($value, $validValues)); // bool(true) WTF???
102 | ```
103 |
104 | PHP 8 will give the string to number comparisons a more reasonable behaviour: When comparing to a numeric string, use a number comparison (same as now). Otherwise, convert the number to string and use a string comparison. Examples:
105 |
106 | ```PHP
107 | var_dump(0 == "0"); // PHP 7: true; PHP 8: true
108 | var_dump(0 == "0.0"); // PHP 7: true; PHP 8: true
109 | var_dump(0 == "foo"); // PHP 7: true; PHP 8: false
110 | var_dump(0 == ""); // PHP 7: true; PHP 8: false
111 | var_dump(42 == " 42"); // PHP 7: true; PHP 8: true
112 | var_dump(42 == "42foo"); // PHP 7: true; PHP 8: false
113 | ```
114 |
115 | ## Make sorting stable
116 |
117 | > RFC: [https://wiki.php.net/rfc/stable_sorting](https://wiki.php.net/rfc/stable_sorting)
118 |
119 | Sorting functions in PHP are currently unstable, which means that the order of “equal” elements is not guaranteed. Example:
120 |
121 | ```php
122 | $array = [
123 | 'c' => 1,
124 | 'd' => 1,
125 | 'a' => 0,
126 | 'b' => 0,
127 | ];
128 | asort($array);
129 | ```
130 |
131 | With stable sorting, we might expect output to be:
132 |
133 | ```bash
134 | ['a' => 0, 'b' => 0, 'c' => 1, 'd' => 1]
135 | ```
136 |
137 | However, since PHP 7 sorting is currently unstable, it could be one of the following as well
138 |
139 | ```bash
140 | ['b' => 0, 'a' => 0, 'c' => 1, 'd' => 1]
141 | ['a' => 0, 'b' => 0, 'd' => 1, 'c' => 1]
142 | ['b' => 0, 'a' => 0, 'd' => 1, 'c' => 1]
143 | ```
144 |
145 | PHP 8 will have stable sorting for sorting functions like: sort, rsort, usort, asort, arsort, uasort, ksort, krsort, uksort, array_multisort.
146 |
147 | ## Reclassifying engine warnings
148 |
149 | > RFC: [https://wiki.php.net/rfc/engine_warnings](https://wiki.php.net/rfc/engine_warnings)
150 |
151 | A lot of warning in PHP 7 will now be converted to Errors (Mostly Error exception or Type Error)
152 |
153 | Please check the RFC to see changes in all warnings of PHP 7 which are converted to Errors in PHP 8.
154 |
155 |
156 |
--------------------------------------------------------------------------------
/content/101-union-types.md:
--------------------------------------------------------------------------------
1 | ## Union types
2 |
3 | > RFC: [https://wiki.php.net/rfc/union_types_v2](https://wiki.php.net/rfc/union_types_v2)
4 |
5 | ### History
6 |
7 | Skip to title `Changes in PHP 8` if you are interested to see only the changes made in PHP 8.
8 |
9 | ### Dynamically typed language
10 |
11 | PHP is a dynamically typed language, that means we can assign any data-type to any variable. Following code is perfectly fine in PHP:
12 |
13 | **File: examples/UnionTypes/DynamicTypes.php**
14 |
15 | ```php
16 | intType = $intType;
56 | }
57 |
58 | public function printValue()
59 | {
60 | echo 'Value of variable is ' . $this->intType . PHP_EOL;
61 | }
62 | }
63 |
64 | $instance = new TypeHinting(2);
65 | $instance->printValue();
66 |
67 | $instance->intType = 2.2;
68 | $instance->printValue();
69 | ```
70 |
71 | As shown in constructor `__construct(int $intType)`, we defined parameter must be of type integer. Thus, if we pass any non-integer parameter to the constructor, it will throw an error and the developer will immediately know and fix it.
72 |
73 | It still did not solve the problem as our instance variable `intType` is public that means, we can still assign any value to it directly. We exactly did that in the line `$instance->intType = 2.2;` and now, it is a float. The correct way is, instance variables must be private or protected and must be managed by functions. Although not recommended, in rare cases, we might need to make an instance variable public. PHP fix that problem in version 7.4 with [typed properties (https://wiki.php.net/rfc/typed_properties_v2)](https://wiki.php.net/rfc/typed_properties_v2). With typed properties, we may rewrite the above example again.
74 |
75 | **File: examples/UnionTypes/TypeHinting2.php**
76 |
77 | ```php
78 | class TypeHinting2
79 | {
80 | public int $intType;
81 |
82 | public function __construct(int $intType)
83 | {
84 | $this->intType = $intType;
85 | }
86 |
87 | public function printValue()
88 | {
89 | echo 'Value of variable is ' . $this->intType . PHP_EOL;
90 | }
91 | }
92 |
93 | $instance = new TypeHinting2(2);
94 | $instance->printValue();
95 |
96 | $instance->intType = 2.2;
97 | $instance->printValue();
98 |
99 | $instance->intType = "3.5 is a float with some extra string";
100 | $instance->printValue();
101 |
102 | $instance->intType = "Now we have a string that can't be casted to int";
103 | $instance->printValue();
104 | ```
105 |
106 | Here, we changed `public $intType;` to `public int $intType;`. With this, we tell PHP that instance variable `$intType` may contain only integer values. Let's see the output of the program before discussing it.
107 |
108 | ```bash
109 | Value of variable is 2
110 |
111 | Value of variable is 2
112 |
113 | Notice: A non well formed numeric value encountered in /home/kapil/dev/ebooks/WhatsNewInPHP8/examples/101-UnionTypes/TypeHinting2.php on line 35
114 | Value of variable is 3
115 |
116 | PHP Fatal error: Uncaught TypeError: Typed property TypeHinting2::$intType must be int, string used in /home/kapil/dev/ebooks/WhatsNewInPHP8/examples/101-UnionTypes/TypeHinting2.php:38
117 | Stack trace:
118 | #0 {main}
119 | thrown in /home/kapil/dev/ebooks/WhatsNewInPHP8/examples/101-UnionTypes/TypeHinting2.php on line 38
120 | ```
121 |
122 | We first set an integer with `$instance = new TypeHinting2(2);` and output was as expected `Value of variable is 2`.
123 |
124 | In next line, we set float value to the variable as `$instance->intType = 2.2;`. Here, PHP did the internal cast and made float to integer. Thus, output was even though not expected as human, it is expected `Value of variable is 2` (Not 2.2)
125 |
126 | Next, we assign a string starting with a integer `$instance->intType = "3.5 is a float with some extra string";`. Now there is a problem but PHP still manage to cast it with notice but the program still executed and continued.
127 |
128 | ```bash
129 | Notice: A non well formed numeric value encountered in /home/kapil/dev/ebooks/WhatsNewInPHP8/examples/101-UnionTypes/TypeHinting2.php on line 35
130 | Value of variable is 3
131 | ```
132 |
133 | **Important: In PHP 8, this will throw TypeError, not warning.**
134 |
135 | In the end, we push PHP to its limit and assigned a string `$instance->intType = "Now we have a string that can't be cast to int";`. Now PHP cannot auto-cast it to an integer and finally throw an error and stop executing program.
136 |
137 | ```bash
138 | Fatal error: Uncaught TypeError: Typed property TypeHinting2::$intType must be int, string used in /home/kapil/dev/ebooks/WhatsNewInPHP8/examples/101-UnionTypes/TypeHinting2.php:38
139 | Stack trace:
140 | #0 {main}
141 | thrown in /home/kapil/dev/ebooks/WhatsNewInPHP8/examples/101-UnionTypes/TypeHinting2.php on line 38
142 | ```
143 |
144 | Even though PHP is dynamically typed language, with above changes, it was making things more strict, to make it easy to identify possible bugs during development time only.
145 |
146 | Everything comes at a price, with type-hinting, we faced a new problem as PHP does not support Method-overloading. Assume we need to make `add` function, which should work on both integer and float. In strictly types language, which supports overloading with different signature (method name with parameter list), the following code should be perfect.
147 |
148 | **File: examples/UnionTypes/Overloading.php**
149 |
150 | ```php
151 | Class Overloading
152 | {
153 | public function add(int $number1, int $number2): int
154 | {
155 | return $number1 + $number2;
156 | }
157 |
158 | public function add(float $number1, float $number2): float
159 | {
160 | return $number1 + $number2;
161 | }
162 | }
163 |
164 | $instance = new Overloading();
165 | echo '1 + 2 = ' . $instance->add(1, 2) . PHP_EOL;
166 | echo '1.1 + 2.2 = ' . $instance->add(1.1, 2.2) . PHP_EOL;
167 | ```
168 |
169 | However, PHP do not support method overloading thus above code throw following error
170 |
171 | ```bash
172 | Fatal error: Cannot redeclare Overloading::add() in /home/kapil/dev/ebooks/WhatsNewInPHP8/examples/101-UnionTypes/Overloading.php on line 25
173 | ```
174 |
175 | Although we can have a workaround of defining add with float and cast result to integer, it is not very convenient. Thus, PHP 8 comes with `Union Types`.
176 |
177 | ### Changes in PHP 8
178 |
179 | With `Union Types`, we can define multiple type hinting for a variable as follow:
180 |
181 | **File: examples/UnionTypes/Overloading.php**
182 |
183 | ```php
184 | Class UnionTypes
185 | {
186 | public function add(int|float $number1, int|float $number2): int|float
187 | {
188 | return $number1 + $number2;
189 | }
190 | }
191 |
192 | $instance = new UnionTypes();
193 | echo '1 + 2 = ' . $instance->add(1, 2) . PHP_EOL;
194 | echo '1.1 + 2.2 = ' . $instance->add(1.1, 2.2) . PHP_EOL;
195 | ```
196 |
197 | Output
198 |
199 | ```bash
200 | 1 + 2 = 3
201 | 1.1 + 2.2 = 3.3
202 | ```
203 |
204 | As seen in the above example, we type hinted $number1, $number2 and return type as `int|float`, which says it could be either integer or float.
205 |
206 | One important thing to note, PHP 7.1 introduced [nullable types (https://www.php.net/manual/en/migration71.new-features.php)](https://www.php.net/manual/en/migration71.new-features.php), where we can define type as `?string` meant it is either String or null. It will not work with union types. However, we can achieve the same result with `string|null`.
207 |
--------------------------------------------------------------------------------