<?php
namespace MyApp\User\Input;
class User
{
pub fn getUserId(): int
{
return $this->userId;
}
}
<?php
namespace MyApp\User\Input;
class User
{
public function getUserId(): int
{
return $this->userId;
}
}
<?php
namespace MyApp\Service;
class SomeService
{
pub fn calculateExpressDeliveryCost(Customer $customer, Order $order): Money
{
...
}
}
<?php
namespace MyApp\Service;
class SomeService
{
public function calculateExpressDeliveryCost(Customer $customer, Order $order): Money
{
...
}
}
12345678901234567890123456789012345678901234567890123456789012345678901234567890
<?php
namespace MyApp\User\Input;
class User{
private int $userId;
public getUserId(): int => $this->userId;
}
<?php
namespace MyApp\User\Input;
class User{
private int $userId;
public function getUserId(): int{
return $this->userId;
}
}
<?php
namespace MyApp\User\Input;
class User{
private int $userId{ get; }
private string $username{ get; set; }
}
<?php
namespace MyApp\User\Input;
class User{
private int $userId;
private string $username;
public function getUserId(): int{
return $this->userId;
}
public function getUsername(): string{
return $this->username;
}
public function setUsername(string $username): void{
$this->username=$username;
}
}
Upgrading
PHP as a language
No corporate sponsor
(almost) no funding
Core devs != PHP devs
PHP is built in C
Example from https://coffeescript.org/#introduction
CoffeeScript -> JavaScript
Example from https://david-barreto.com/introduction-to-the-typescript-transpiler/
TypeScript -> JavaScript
Elixir & Erlang
Transpiling PHP
Miro Svrtan
@msvrtan
is an umbrella term to describe a program that takes source code written in one language and produce a (or many) output file in some other language. In practice we mostly use this term to describe a compiler such as gcc which takes in C code as input and produces a binary executable (machine code) as output.
Compiler:
Source: https://stackoverflow.com/questions/44931479/compiling-vs-transpiling
are also known as source-to-source compilers. So in essence they are a subset of compilers which take in a source code file and convert it to another source code file in some other language or a different version of the same language. The output is generally understandable by a human. This output still has to go through a compiler or interpreter to be able to run on the machine.
Transpiler:
Source: https://stackoverflow.com/questions/44931479/compiling-vs-transpiling
HHVM
https://www.youtube.com/watch?v=TOveFBc9AyI
Sara Golemon-
The influence HHVM had on PHP
https://www.youtube.com/watch?v=QutoZvrmHbo
Sara Golemon -
HHVM and Hack
Steps:
lexical analysis
syntax analysis
semantic analysis
code generation
Lexical analysis:
converts source code to tokens
Lexical analysis:
<?php
namespace MyApp\User\Input;
class User
{
public function getUserId(): int
{
return $this->userId;
}
}
...
6 => [
0 => 390, // <- T_NS_SEPARATOR
1 => '\\',
2 => 1,
],
7 => [
0 => 319, // <- T_STRING
1 => 'Input',
2 => 1,
],
8 => ';',
9 => [
0 => 361, // <- T_CLASS
1 => 'class',
2 => 1,
],
10 => [
0 => 382, // <- T_WHITESPACE
1 => ' ',
2 => 1,
],
11 => [
0 => 319, // <- T_STRING
1 => 'User',
2 => 1,
],
12 => '{',
...
Syntax analysis:
builds Abstract Syntax Tree
checks if the code structure is right
Syntax analysis:
<?php
namespace MyApp\User\Input;
abstract class User
{
public function getUserId(): int
{
return $this->userId;
}
public static function getSomething()...
}
<?php
namespace MyApp\User\Input;
class User abstract
{
public getUserId(): int
{
$this->userId return;
}
function public static getSomething()...
}
Semantic analysis:
additional code checks
https://www.youtube.com/watch?v=AEfkYUjEuSs
James Titcumb -
Climbing the Abstract Syntax Tree
https://2019.webcampzg.org/talks/parsing-from-first-principles/
Saša Jurić -
Parsing from first principles
nikic/php-parser
https://github.com/nikic/PHP-Parser
Nikita Popov
Steps:
lexical analysis
syntax analysis
semantic analysis
code generation
token_get_all()
nikic/php-parser
My
AST generator
My
lexer,parser
& code generator
Var 1:
lexical analysis
syntax analysis
semantic analysis
code generation
custom lexer
nikic/php-parser
<?php
namespace MyApp\User\Input;
class User
{
pub fn getUserId(): int
{
return $this->userId;
}
}
<?php
namespace MyApp\User\Input;
class User
{
public function getUserId(): int
{
return $this->userId;
}
}
pub fn getUserId
public function getUserId
...
T_PUBLIC
T_FUNCTION
T_STRING
..
Custom lexer in PHP
- full PHP support
- slow
Custom lexer in RUST
Pre
https://preprocess.io/
Christopher Pitt
PHP Plus
https://php-plus.com
Nuno Maduro
Yay
https://github.com/marcioAlmada/yay
Márcio Almada
Zephir
https://github.com/phalcon/zephir
Problemz
- IDE support
- performance
<?php
namespace MyApp\User\Input;
use MyApp\User\UserId;
class UserRegistrationDetails{
/** @var UserId */
private $userId;
/** @var string */
private $username;
/** @var string */
private $password;
/** @var string */
private $emailAddress;
public function __construct(
UserId $userId, string $username, string $password, string $emailAddress
) {
$this->userId = $userId;
$this->username = $username;
$this->password = $password;
$this->emailAddress = $emailAddress;
}
public function getUserId() : UserId{
return $this->userId;
}
public function getUsername() : string{
return $this->username;
}
public function getPassword() : string{
return $this->password;
}
public function getEmailAddress() : string{
return $this->emailAddress;
}
}
<?php
namespace MyApp\User\Input;
use MyApp\User\UserId;
struct UserRegistrationDetails{
UserId $userId;
string $username;
string $password;
string $emailAddress;
}
<?php
namespace MyApp\User\Input;
use MyApp\User\UserId;
class UserRegistrationDetails{
/** @var UserId */
private $userId;
/** @var string */
private $username;
/** @var string */
private $password;
/** @var string */
private $emailAddress;
public function __construct(
UserId $userId, string $username, string $password, string $emailAddress
) {
$this->userId = $userId;
$this->username = $username;
$this->password = $password;
$this->emailAddress = $emailAddress;
}
public function getUserId() : UserId{
return $this->userId;
}
public function getUsername() : string{
return $this->username;
}
public function getPassword() : string{
return $this->password;
}
public function getEmailAddress() : string{
return $this->emailAddress;
}
}
<?php
namespace MyApp\User\Input;
use MyApp\User\UserId;
struct UserRegistrationDetails{
UserId $userId;
string $username;
string $password;
string $emailAddress;
}
<?php
namespace MyApp\User\Input;
use MyApp\User\UserId;
struct UserRegistrationDetails{
UserId $userId;
string $username;
}
<?php
namespace MyApp\User\Input;
use MyApp\User\UserId;
class UserRegistrationDetails
{
/** @var UserId */
private $userId;
/** @var string */
private $username;
/** @param string $username */
public function __construct(UserId $userId, $username)
{
$this->userId = $userId;
$this->username = $username;
}
/** @return UserId */
public function getUserId()
{
return $this->userId;
}
/** @return string */
public function getUsername()
{
return $this->username;
}
}
PHP < 7.0
<?php
namespace MyApp\User\Input;
use MyApp\User\UserId;
class UserRegistrationDetails
{
/** @var UserId */
private $userId;
/** @var string */
private $username;
public function __construct(UserId $userId, string $username) {
$this->userId = $userId;
$this->username = $username;
}
public function getUserId(): UserId
{
return $this->userId;
}
public function getUsername(): string
{
return $this->username;
}
}
PHP 7.0 / 7.1 / 7,2 / 7,3
<?php
namespace MyApp\User\Input;
use MyApp\User\UserId;
class UserRegistrationDetails
{
private UserId $userId;
private string username;
public function __construct(UserId $userId, string $username) {
$this->userId = $userId;
$this->username = $username;
}
public function getUserId(): UserId
{
return $this->userId;
}
public function getUsername(): string
{
return $this->username;
}
}
PHP 7.4
<?php
namespace MyApp\User\Input;
use MyApp\User\UserId;
immutable class UserRegistrationDetails
{
public UserId $userId;
public string username;
public function __construct(UserId $userId, string $username) {
$this->userId = $userId;
$this->username = $username;
}
}
Immutability RFC
<?php
namespace MyApp\User\Input;
use MyApp\User\UserId;
immutable UserRegistrationDetails
{
UserId $userId;
string username;
}
Immutability RFC
<?php
namespace MyApp\Checkout\PaymentMethod;
enum PaymentMethodType
{
const CREDIT_CARD = 'cc';
const PAYPAL = 'pp';
const CASH = 'cash';
const SEPA = 'sepa';
}
<?php
namespace MyApp\Checkout\PaymentMethod;
use MyCLabs\Enum\Enum;
class PaymentMethodType extends Enum
{
const CREDIT_CARD = 'cc';
const PAYPAL = 'pp';
const CASH = 'cash';
const SEPA = 'sepa';
public static function CREDIT_CARD(): self
{
return new self(self::CREDIT_CARD);
}
public static function PAYPAL(): self
{
return new self(self::PAYPAL);
}
public static function CASH(): self
{
return new self(self::CASH);
}
public static function SEPA(): self
{
return new self(self::SEPA);
}
}
<?php
namespace MyApp\Checkout\PaymentMethod;
class PaymentMethodType
{
const CREDIT_CARD = 'cc';
const PAYPAL = 'pp';
const CASH = 'cash';
const SEPA = 'sepa';
public static function CREDIT_CARD(): self
{
return new self(self::CREDIT_CARD);
}
public static function PAYPAL(): self
{
return new self(self::PAYPAL);
}
public static function CASH(): self
{
return new self(self::CASH);
}
public static function SEPA(): self
{
return new self(self::SEPA);
}
// CONTENT OF ENUM CLASS FROM
// https://github.com/myclabs/php-enum/blob/master/src/Enum
}
<?php
namespace MyApp\Checkout\Payment;
enum PaymentMethodType
{
const CREDIT_CARD = 'cc';
const PAYPAL = 'pp';
const CASH = 'cash';
const SEPA = 'sepa';
}
enum PaymentProvider
{
COMPANY1: 'c1';
COMPANY2: 'c2';
COMPANY3: 'c3';
COMPANY4: 'c4';
COMPANY5: 'c5';
}
enum PaymentStatus
{
PENDING;
PAID;
CANCELLED;
REFUNDED;
}
<?php
namespace MyApp\Checkout\Payment;
enum PaymentStatus
{
const PENDING = 1;
const PAID = 2;
const CANCELLED = 3;
const REFUNDED = 4;
function smallIconFileName():string{
match $this->value{
PENDING : "pending.png",
PAID : "paid.jpeg",
CANCELLED : "cancelled.gif",
REFUNDED : "refunded.png",
}
}
}
<?php
namespace MyApp\Checkout\Payment;
class PaymentStatus extends Enum
{
const PENDING = 1;
const PAID = 2;
const CANCELLED = 3;
const REFUNDED = 4;
...
public function smallIconFileName(): string
{
switch ($this->value) {
case self::PENDING:
return 'pending.png';
case self::PAID:
return 'paid.jpeg';
case self::CANCELLED:
return 'cancelled.gif';
case self::REFUNDED:
return 'refunded.png';
}
}
}
<?php
function read_password_from_config()
{
$filePath = "/path/config.json";
$content = read_file_content($filePath);
$data = decode_json($content);
return get_value($data,"password");
}
function read_file_content(string $filePath): string{
..
}
function decode_json(string $content): array{
..
}
function get_value(array $data, string $key): string{
..
}
Pipe Operator
https://wiki.php.net/rfc/pipe-operator
<?php
function read_password_from_config()
{
return "/path/config.json"
|> read_file_content()
|> decode_json()
|> get_value("password")
}
<?php
function read_password_from_config()
{
$filePath = "/path/config.json";
$content = read_file_content($filePath);
$data = decode_json($content);
return get_value($data,"password");
}
<?php
function read_password_from_config()
{
return get_value(
decode_json(
read_file_content(
"/path/config.json"
)
)
,"password"
);
}
<?php
function read_password_from_config()
{
return "/path/config.json"
|> read_file_content()
|> decode_json()
|> get_value("password")
}
Recap
Thank you!
Miro Svrtan
@msvrtan
Feedback -> https://joind.in/talk/55180
miro (at) mirosvrtan.me
Any questions?
Transpiling PHP
By Miro Svrtan
Transpiling PHP
- 2,265