<?php
// PHP 5.2
function is_palindrome(string $string)
{
return $string === strrev($string);
}
is_palindrome('PHP');
// Catchable fatal error: Argument 1 passed to is_palindrome()
// must be an instance of string, string given
<?php
class ScalarTypehintHandler
{
const TYPEHINT_PCRE = '/^Argument (\d)+ passed to (?:(\w+)::)?(\w+)\(\) must be an instance of (\w+), (\w+) given/';
private static $typehints = [
'bool' => 'is_bool',
'int' => 'is_int',
'float' => 'is_float',
'string' => 'is_string',
'resource' => 'is_resource',
];
private static function getTypehintedArgument($backtrace, $function, $argIndex, &$argValue)
{
foreach ($backtrace as $trace) {
if (isset($trace['function']) && $trace['function'] == $function) {
$argValue = $trace['args'][$argIndex - 1];
return true;
}
}
return false;
}
public static function handleTypehint($errorLevel, $errorMessage)
{
if ($errorLevel == E_RECOVERABLE_ERROR) {
if (preg_match(static::TYPEHINT_PCRE, $errorMessage, $errorMatches)) {
list($errorMatch, $argIndex, $class, $function, $hint, $type) = $errorMatches;
if (isset(static::$typehints[$hint])) {
$backtrace = debug_backtrace();
$argValue = null;
if (static::getTypehintedArgument($backtrace, $function, $argIndex, $argValue)) {
if (call_user_func(static::$typehints[$hint], $argValue)) {
return true;
}
}
}
}
}
return false;
}
}
set_error_handler('ScalarTypehintHandler::handleTypehint');
<?php
// PHP 5.2
require __DIR__ . '/ScalarTypehintHandler.php';
function is_palindrome(string $string)
{
return $string === strrev($string);
}
is_palindrome('PHP');
// true
<?php
$i = 0;
$i = 'foobar';
<?php
function int(int $i): int
{
return $i;
}
$i = int(0);
$i = 'foobar';
<?php
function int(int $i): int
{
$obj = new class {
public int $prop;
};
$obj->prop = $i;
return $obj->prop;
}
$i = int(0);
$i = 'foobar';
<?php
function &int(int $i): int
{
$obj = new class {
public int $prop;
};
$obj->prop = $i;
return $obj->prop;
}
$i =& int(0);
$i = 'foobar';
<?php
function &int(int $i): int
{
$obj = new class {
public int $prop;
};
$obj->prop = $i;
$GLOBALS['typed_variables'][] = $obj;
return $obj->prop;
}
$i =& int(0);
$i = 'foobar';
<?php
function &int(int $i): int
{
$obj = new class {
public int $prop;
};
$obj->prop = $i;
$GLOBALS['typed_variables'][] = $obj;
return $obj->prop;
}
$i =& int(0);
$i = 'foobar';
// Uncaught TypeError: Cannot assign string to reference
// held by property class@anonymous::$prop of type int
<?php
function &int(int $i): int
{
$obj = new class {
public int $prop;
};
$obj->prop = $i;
$GLOBALS['typed_variables'][] = $obj;
return $obj->prop;
}
$i =& int(0);
$i = 12;
// ✅
$i = 14;
// ✅
$i = false;
// ⛔️
<?php
$eventID = array_pop(explode('/', trim(parse_url('https://www.meetup.com/php-sw/events/272037512/', PHP_URL_PATH), '/')));
// "272037512"
<?php
$eventID = array_pop(
explode(
'/',
trim(
parse_url('https://www.meetup.com/php-sw/events/272037512/', PHP_URL_PATH),
'/'
)
)
);
// "272037512"
<?php
$url = 'https://www.meetup.com/php-sw/events/272037512/';
$path = parse_url($url, PHP_URL_PATH);
$pathWithTrimmedSlashes = rtrim($path, '/');
$pathSegments = explode('/', $pathWithTrimmedSlashes);
$eventID = array_pop($pathSegments);
// "272037512"
<?php
$eventID = 'https://www.meetup.com/php-sw/events/272037512/'
|> parse_url($$, PHP_URL_PATH)
|> rtrim($$, '/')
|> explode('/', $$)
|> array_pop($$);
// "272037512"
<?php
$eventID = pipe('https://www.meetup.com/php-sw/events/272037512/')
-> parse_url('$$', PHP_URL_PATH)
-> rtrim('$$', '/')
-> explode('/', '$$')
-> array_pop('$$')
-> get();
// "272037512"
<?php
function pipe($value)
{
return new class($value) {
protected $value;
public function __construct($value)
{
$this->value = $value;
}
public function __call($function, $arguments)
{
foreach ($arguments as &$argument) {
if ($argument === '$$') {
$argument = $this->value;
}
}
$newValue = call_user_func($function, ...$arguments);
return pipe($newValue);
}
public function get()
{
return $this->value;
}
};
}
<?php
$eventID = pipe('https://www.meetup.com/php-sw/events/272037512/')
-> parse_url('$$', PHP_URL_PATH)
-> rtrim('$$', '/')
-> explode('/', '$$')
-> array_pop('$$')
-> get();
// "272037512"
<?php
class Countries
{
public static function getValues()
{
return json_decode(file_get_contents(__DIR__ . '/countries.json'));
}
}
Countries::getValues();
<?php
class Countries
{
protected static array $values;
public static function getValues()
{
if (!isset($static::$values)) {
static::$values = json_decode(file_get_contents(__DIR__ . '/countries.json'));
}
return static::$values;
}
}
Countries::getValues();
takes no parameters
should only be called once
is executed BEFORE any instances of the class are created
new Example();
is executed BEFORE any static properties are referenced
Example::$value;
is executed BEFORE any other static methods are called
Example::test();
<?php
class Countries
{
public static array $values = [];
public static function __constructStatic()
{
static::$values = json_decode(file_get_contents(__DIR__ . '/countries.json'));
}
}
<?php
class Countries
{
public static array $values = [];
public static function __constructStatic()
{
static::$values = json_decode(file_get_contents(__DIR__ . '/countries.json'));
}
}
Countries::__constructStatic();
Countries.php
<?php
class UserIndexController
{
public function __invoke()
{
// ...
}
}
UserIndexController.php
<?php
Route::get('/', HomepageController::class);
Route::get('/users', UserIndexController::class);
Route::get('/login', Auth\LoginController::class);
Route::post('/logout', Auth\LogoutController::class);
routes/web.php
<?php
class UserIndexController
{
public function __constructStatic()
{
Route::get('/users', UserIndexController::class);
}
public function __invoke()
{
// ...
}
}
UserIndexController::__constructStatic();
UserIndexController.php
<?php
$user = new User();
// Uncaught Error: Class 'User' not found
<?php
spl_autoload_register(function ($className) {
require_once "{$className}.php";
});
$user = new User();
// require_once 'User.php';
<?php
require_once __DIR__ . '/vendor/autoload.php';
spl_autoload_register(function ($className) {
if (class_exists($className, autoload: false)) {
if (method_exists($className, '__constructStatic')) {
$className::__constructStatic();
}
}
});
<?php
class UserIndexController
{
public function __invoke()
{
// ...
}
}
<?php
@@Route('get', '/users')
class UserIndexController
{
public function __invoke()
{
// ...
}
}
<?php
@@Attribute(Attribute::TARGET_CLASS)
class Route
{
protected string $method;
protected string $route;
public function __construct(string $method, string $route) {
$this->method = $method;
$this->route = $route;
}
}
<?php
spl_autoload_register(function ($className) {
$reflectionClass = new ReflectionClass($className);
$attributes = $r->getAttributes();
foreach ($attributes as $attribute) {
if ($attribute->getName() === Route::class) {
$route = $attribute->newInstance();
}
}
});
<?php
@@Attribute(Attribute::TARGET_CLASS)
class Route
{
protected string $method;
protected string $route;
public function __construct(string $method, string $route) {
$this->method = $method;
$this->route = $route;
}
public function register(string $controller)
{
Route::{$this->method}($this->route, $controller);
}
}
<?php
spl_autoload_register(function ($className) {
$reflectionClass = new ReflectionClass($className);
$attributes = $r->getAttributes();
foreach ($attributes as $attribute) {
if ($attribute->getName() === Route::class) {
$route = $attribute->newInstance();
$route->register($className);
}
}
});
<?php
@@Route('get', '/users')
class UserIndexController
{
public function __invoke()
{
return view('users.index', [
'users' => User::all(),
]);
}
}
<?php
class Email
{
protected string $to;
protected string $subject = '(No subject)';
protected string $body = '';
public function to(string $to)
{
$this->to = $to;
return $this;
}
public function subject(string $subject)
{
$this->subject = $subject;
return $this;
}
public function body(string $body)
{
$this->body = $body;
return $this;
}
public function send()
{
mail($this->to, $this->subject, $this->body);
}
}
<?php
$email = new Email();
$email->to('info@phpsw.uk')
->body('Hi folks...')
->subject('Let me talk about Unconventional PHP!')
->send();
<?php
class EmailFacade
{
public function __callStatic($name, $arguments)
{
$email = new Email();
return $email->{$name}(...$arguments);
}
}
EmailFacade::body('Hi folks...')
->to('info@phpsw.uk')
->subject('Let me talk about Unconventional PHP!')
->send();
<?php
spl_autoload_register(
autoload_function: function($className) {
// ...
},
throw: true,
prepend: true
);
<?php
spl_autoload_register(
autoload_function: function($className) {
if (str_starts_with($className, 'Facades\\')) {
$path = __DIR__ . '/Facades/' . sha1($className) . '.php';
// ...
}
},
throw: true,
prepend: true
);
<?php
spl_autoload_register(
autoload_function: function($className) {
if (str_starts_with($className, 'Facades\\')) {
$path = __DIR__ . '/Facades/' . sha1($className) . '.php';
if (file_exists($path)) {
require_once $path;
return true;
}
// ...
}
},
throw: true,
prepend: true
);
<?php
spl_autoload_register(
autoload_function: function($className) {
if (str_starts_with($className, 'Facades\\')) {
$path = __DIR__ . '/Facades/' . sha1($className) . '.php';
if (file_exists($path)) {
require_once $path;
return true;
}
if (!is_dir(__DIR__ . '/Facades')) {
mkdir(__DIR__ . '/Facades');
}
touch($path);
// ...
}
},
throw: true,
prepend: true
);
<?php
spl_autoload_register(
autoload_function: function($className) {
if (str_starts_with($className, 'Facades\\')) {
$path = __DIR__ . '/Facades/' . sha1($className) . '.php';
if (file_exists($path)) {
require_once $path;
return true;
}
if (!is_dir(__DIR__ . '/Facades')) {
mkdir(__DIR__ . '/Facades');
}
touch($path);
$realClassName = substr($className, strlen('Facades\\'));
// ...
}
},
throw: true,
prepend: true
);
<?php
spl_autoload_register(
autoload_function: function($className) {
if (str_starts_with($className, 'Facades\\')) {
$path = __DIR__ . '/Facades/' . sha1($className) . '.php';
if (file_exists($path)) {
require_once $path;
return true;
}
if (!is_dir(__DIR__ . '/Facades')) {
mkdir(__DIR__ . '/Facades');
}
touch($path);
$realClassName = substr($className, strlen('Facades\\'));
file_put_contents($path, <<<EOL
<?php
namespace Facades;
class {$realClassName}
{
public static function __callStatic(\$name, \$arguments)
{
\$class = new \\{$realClassName};
return \$class->{\$name}(...\$arguments);
}
}
EOL);
require_once $path;
return true;
}
},
throw: true,
prepend: true
);
<?php
spl_autoload_register(
autoload_function: function($className) {
if (str_starts_with($className, 'Facades\\')) {
$path = __DIR__ . '/Facades/' . sha1($className) . '.php';
if (file_exists($path)) {
require_once $path;
return true;
}
if (!is_dir(__DIR__ . '/Facades')) {
mkdir(__DIR__ . '/Facades');
}
touch($path);
$realClassName = substr($className, strlen('Facades\\'));
file_put_contents($path, <<<EOL
<?php
namespace Facades;
class {$realClassName}
{
public static function __callStatic(\$name, \$arguments)
{
\$class = new \\{$realClassName};
return \$class->{\$name}(...\$arguments);
}
}
EOL);
require_once $path;
return true;
}
},
throw: true,
prepend: true
);
<?php
require_once __DIR__ . '/realtime_facade_autoload.php';
Facades\Email::to('info@phpsw.uk')
->body('Hi folks...')
->subject('Let me talk about Unconventional PHP!')
->send();
src/
├── Email.php
├── index.php
├── realtime_facade_autoload.php ├── Facades/ │ ├── 84add5b2952787581cb9a8851eef63d1ec75d22b.php
class Fixture
{
private $name = ucwords("acme fixture");
private $thing = new \stdClass();
}
function example($one, $two = 2.2, $three = round(3.3), $four = new stdClass) { ... }
async function get_file(string $path)
{
await \Amp\File\get($path);
}
$users = { $user1, $user2, $user3 };
$admins = $users->filter(fn($user) => $user->isAdmin());
class Collection<T> {
protected $items = [];
public function __construct($items = [])
{
$this->items = $items;
}
public function add(T $item)
{
$this->items[] = $item;
}
}
$users = new Collection<User>;
$users->add($user1); // ✅
$users->add('Foo'); // ⛔︎
$file = fopen('file.txt');
defer fclose($file);
// Do stuff with $file ...
// The file will automatically be closed at the end
function MyForm($props) {
return (
<form>
{$props->showLabel ? <label htmlFor={"email"}>Email</label> : null}
<input type={"text"} name={"email"} id={"email"} />
</form>
);
}
https://github.com/runkit7/runkit7
https://www.swoole.co.uk/
<?php
class StringHandler {
public static function length($self) {
return strlen($self);
}
public static function startsWith($self, $other) {
return strpos($self, $other) === 0;
}
}
register_primitive_type_handler('string', StringHandler::class);
$string = "abc";
var_dump($string->length()); // int(3)
var_dump($string->startsWith("a")); // bool(true)
+
-
>
&&
<=
*
<=>
<?php
$eighteenYearsAgo = new DateTime('-18 years');
$dateOfBirth = new DateTime('1995-07-15');
if ($dateOfBirth > $eighteenYearsAgo) {
throw new Exception('You are too young to buy this product');
}
<?php
class Money
{
public int $value;
public string $currency;
public function __construct(int $value, string $currency)
{
$this->value = $value;
$this->currency = $currency;
}
public function __add(Money $money)
{
$convertedValue = CurrencyConverter::from($money->currency, $money->value)
->to($this->currency);
return new Money($this->value + $money->value, $this->currency);
}
}
$bank = new Money(6350, 'GBP');
$wallet = new Money(200, 'USD');
$total = $bank + $wallet; // 6503 GBP
slides.com/liamhammett/unconventional-php