Skip to content

Instantly share code, notes, and snippets.

@E1101
Last active April 28, 2021 06:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save E1101/b05e913e26bdf7fb6c01 to your computer and use it in GitHub Desktop.
Save E1101/b05e913e26bdf7fb6c01 to your computer and use it in GitHub Desktop.
PHP New Features
<?php
## --------------------------------------------------
## PHP ^7.4
## --------------------------------------------------
##
# Typed properties
#
class User {
public int $id;
public string $name;
}
##
# Arrow functions
#
$factor = 10;
$nums = array_map(fn($n) => $n * $factor, [1, 2, 3, 4]);
// $nums = array(10, 20, 30, 40);
##
# Null coalescing assignment operator
#
$array['key'] ??= computeDefault();
// is roughly equivalent to
if (!isset($array['key']))
$array['key'] = computeDefault();
$value = $object->property ?? 'fallback if null';
$value = $array['foo'] ?? "fallback if key doesn't exists";
##
# Unpacking inside arrays
#
$parts = ['apple', 'pear'];
$fruits = ['banana', 'orange', ...$parts, 'watermelon'];
// ['banana', 'orange', 'apple', 'pear', 'watermelon'];
$a = [/* … */];
$b = [/* … */];
$mergedArray = [...$a, ...$b];
##
# Allow exceptions from __toString()
#
## --------------------------------------------------
## PHP ~7.0
## --------------------------------------------------
##
# Scalar type declarations
#
declare(strict_types=0);
function add(int $a, int $b) {
return $a + $b;
}
##
# Return type declarations
#
declare(strict_types=1); // strict type checking
function add(int $a, int $b): int {
return (string)($a + $b);
}
##
# Nullable types
#
function welcome(?string $name) {
echo $name;
}
welcome(); // error
welcome(null); // valid
function welcome($name): ?string
{
return null; // valid
}
##
# Void return types
#
function A(): void {
// valid
}
function B(): void {
return; // valid
}
function C(): void {
return null; // invalid
}
##
# Class constant visibility modifiers
#
class Visibility
{
// Constants without defined visibility
const THE_DEFAULT_PUBLIC_CONST = 'PHP';
// Constants with defined visibility
private const THE_PRIVATE_CONST = 'PHP';
protected const THE_PROTECTED_CONST = 'PHP';
public const THE_PUBLIC_CONST = 'PHP';
}
##
# Anonymous classes
#
$foo = new class {
public function foo() {
return "bar";
}
};
##
# Closure::call()
#
class Foo
{
private $foo = 'bar';
}
$getFooCallback = function() {
return $this->foo;
};
//PHP5 style
$binding = $getFooCallback->bindTo(new Foo,'Foo');
echo $binding().PHP_EOL;
//PHP7 style
echo $getFooCallback->call(new Foo).PHP_EOL;
##
# Generator delegation
#
function gen()
{
yield 1;
yield 2;
yield from gen2(); // <= delegate from another generator
}
function gen2()
{
yield 3;
yield 4;
}
foreach (gen() as $val)
{
echo $val, PHP_EOL;
}
##
# Generator return expressions
#
$gen = (function() {
yield 1;
yield 2;
return 3;
})();
foreach ($gen as $val) {
echo $val, PHP_EOL;
}
echo $gen->getReturn(), PHP_EOL; // <= return part
##
# Null coalesce operator
#
$array = ['foo'=>'bar'];
//PHP5 style
$message = isset($array['foo']) ? $array['foo'] : 'not set';
echo $message.PHP_EOL;
//PHP7 style
$message = $array['foo'] ?? 'not set'; // <= null coalesce
echo $message.PHP_EOL;
// or chained
$bar = $foo ?? $baz ?? 'default';
##
# Space ship operator
#
$array = [
"1 <=> 1" => 1 <=> 1, // 0 when both values are equal
"1 <=> 2" => 1 <=> 2, // -1 when the left value is less than the right value
"2 <=> 1" => 2 <=> 1, // 1 if the left value is greater than the right value
];
##
# Level support for the dirname() function
#
// would echo '/var/www/html/app/etc'
echo dirname('/var/www/html/app/etc/config.php');
// would echo '/var/www/html/app'
echo dirname('/var/www/html/app/etc/config.php', 2);
##
# Constant arrays
#
// The class constant - using 'const' keyword
class Rift {
const APP = [
'name' => 'Rift',
'edition' => 'Community',
'version' => '2.1.2',
'licence' => 'OSL'
];
}
// The constant - using 'define' construct
define('APP', [
'name' => 'Rift',
'edition' => 'Community',
'version' => '2.1.2',
'licence' => 'OSL'
]);
echo Rift::APP['version'];
echo APP['version'];
##
# Catching multiple exceptions types
#
try {
// ...
}
catch (\InvalidArgumentException $e)
{
// ...
}
catch (Exception $e)
{
// ...
}
finally
{
// ...
}
try {
// ...
}
catch (\InvalidArgumentException | \LengthException $e)
{
// ...
}
##
# Group Import Declarations
#
use Framework\Module\Foo;
use Framework\Module\Bar;
use Framework\Module\Baz;
//PHP7 style
use Framework\Module\{Foo, Bar, Baz};
##
# Iterable pseudo-type
#
function import(iterable $users = null)
{
// ...
}
function mix(): iterable {
return [
'Welcome',
33,
4200.00
];
}
function numbers(): iterable {
for ($i = 0; $i <= 5; $i++) {
yield $i;
}
}
##
# Unicode Codepoint Escape Syntax
#
echo "\u{1F602}"; // outputs 😂‚
##
# Allow trailing comma in function and method calls
#
foo('bar', 'baz',);
##
# Option to make json_encode and json_decode throw exceptions on errors
#
try {
json_decode("{", false, 512, JSON_THROW_ON_ERROR);
}
catch (\JsonException $exception) {
echo $exception->getMessage(); // echoes "Syntax error"
}
##
# Changes In list()
#
// References in list()
$arr = ['apple', 'orange'];
list($a, &$b) = $arr;
$b = 'banana';
echo $arr[1];
# array(3) {
# [0]=> string(5) "blue"
# [1]=> string(6) "yellow"
# }
list($colors[], $colors[]) = ['green', 'yellow'];
##
# Session options
#
// Php7 will accept options instead of from php.ini
session_start([
'name' =>'THEAPP',
'cookie_lifetime' => 3600,
'cookie_httponly' => 1,
'lazy_write' => 1
]);
##
# Introduced is_countable() function
#
is_countable($x);
##
# Array destructuring
#
$members = [
[1, 'Seb'],
[2, 'Alex'],
];
foreach ($members as [$id, $name]) {
// do stuff with $id and $name
}
$members = [
['id' => 1, 'name'=> 'Seb', 'twitter' => '@sebdedeyne' ],
];
foreach ($members as ['twitter' => $twitterHandle, 'name' => $firstName]) {
// do stuff with $twitterHandle and $firstName
}
##
# Context sensitive lexer
#
class ReportPool {
public function include(Report $report) {
//
}
public function list() {
//
}
public static function new(array $items) {
return new self($items);
}
}
class Customer {
const class = 'Retail'; // Fatal error
}
##
# Heredoc and Nowdoc syntax requirements are more relaxed
#
$foo = ['foo', 'bar', <<<EOT
baz
- hello world! --
ahoy
EOT, 'qux', 'quux'
];
<?php
## --------------------------------------------------
## PHP ^8.0
## --------------------------------------------------
##
# Constructor Property Promotion
#
class FormRenderer
{
private User $currentUser;
public function __construct(
private ThemeSystem $theme,
private UserRepository $users,
private ModuleSystem $modules,
) {
$this->currentUser = $this->users->getActiveUser();
}
public function renderForm(Form $form): string
{
// ...
}
}
##
# Named Arguments
#
$numbers = [1, 2, 3, 4, 5, 6];
$evens = array_filter($numbers, function($n){
return $n % 2 === 0;
});
$squares = array_map(array: $numbers, callback: function($n) {
return $n * $n;
});
##
# Union Types
#
class SampleClass {
public int|float $number;
function setNumber(int|float $number) {
$this->number = $number;
}
function getNumber(): int|float {
return $this->number;
}
}
##
# Null-Safe Operator
#
$country = null;
if ($session !== null) {
$user = $session->user;
if ($user !== null) {
$address = $user->getAddress();
if ($address !== null) {
$country = $address->country;
}
}
}
// php8.0
$country = $session?->user?->getAddress()?->country;
##
# Trailing Comma
#
function hello($name, $age) {
echo "Hello $name";
}
hello(
'Zura',
28,
);
##
# Match Expression
#
$statusCode = 400;
switch ($statusCode) {
case 200:
case 300:
$message = null;
break;
case 500:
$message = 'server error';
break;
default:
$message = 'unknown status code';
break;
}
// php8.0
$message = match ($statusCode) {
200,
300 => null,
500 => 'server error',
default => 'unknown status code',
};
##
# Attributes
#
#[Attribute]
class ProductSubscriber
{
#[
ListenTo( \app\attributes\Product::EVENT_CREATED ),
\app\attributes\SampleAttribute,
]
public function onProductCreated(ProductCreated $event)
{ /* … */
}
#[ListenTo( \app\attributes\Product::EVENT_DELETED )]
public function onProductDeleted(ProductDeleted $event)
{ /* … */
}
}
$reflectionClass = new ReflectionClass(ProductSubscriber::class);
$m = $reflectionClass->getMethod('onProductCreated');
echo '<pre>';
var_dump($m->getAttributes()[1]->getName());
echo '</pre>';
// All different variations of attributes
#[ClassAttribute]
#[AttributeWithScalarExpression(1 + 1)]
#[AttributeWithClassNameAndConstants(PDO::class, PHP_VERSION_ID)]
#[AttributeWithClassConstant(Http::POST)]
#[AttributeWithBitShift(4 >> 1, 4 << 1)]
class MyClass
{
#[PropertyAttribute]
public int $foo;
#[ConstAttribute]
public const BAR = 1;
/** @return void */
#[MethodAttribute]
/** @return void */
public function doSomething(#[ArgumentAttribute] $bar): void
{
$closure = #[ClosureAttribute] fn() => 1;
}
}
$object = new #[ObjectAttribute] class () { /* … */
};
#[FunctionAttribute]
function foo()
{ /* … */
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment