<?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

miro (at) mirosvrtan.me

Any questions?

Transpiling PHP

By Miro Svrtan

Transpiling PHP

  • 1,966