├── .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 |
2 | 3 | 164 | 165 |
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 | --------------------------------------------------------------------------------