PHP generics written in PHP

Overview

PHP generics written in PHP

Require

  • PHP >= 7.4
  • Composer (PSR-4 Autoload)

Table of contents

How it works

  • parse generics classes;
  • generate concrete classes based on them;
  • say to "composer autoload" to load files from directory with generated classes first and then load classes from main directory.

Quick start

Install library

composer require mrsuh/php-generics

Add directory("cache/") to composer autoload PSR-4 for generated classes. It should be placed before the main directory.

composer.json

{
    "autoload": { 
        "psr-4": {
            "App\\": ["cache/","src/"]
        }
    }
}

Generate concrete classes from generic classes with composer dump-generics command

composer dump-generics -vv

Generate vendor/autoload.php with composer dump-autoload command

composer dump-autoload

Example

!! You can find repository with this example here.

For example, you need to add several PHP files:

  • generic class Box
  • class Usage for use generic class
  • script with composer autoload and Usage class

src/Box.php

<?php

namespace App;

class Box<T> {

    private ?T $data = null;

    public function set(T $data): void {
        $this->data = $data;
    }

    public function get(): ?T {
        return $this->data;
    }
}

src/Usage.php

<?php

namespace App;

class Usage {

    public function run(): void
    {
        $stringBox = new Box<string>();
        $stringBox->set('cat');
        var_dump($stringBox->get()); // string "cat"

        $intBox = new Box<int>();
        $intBox->set(1);
        var_dump($intBox->get()); // integer 1
    }
}

bin/test.php

<?php

require_once __DIR__ . '/../vendor/autoload.php';

use App\Usage;

$usage = new Usage();
$usage->run();

Generate concrete classes from generic classes with composer dump-generics command

composer dump-generics -vv

What the composer dump-generics command does?

  • finds all generic uses in classes (src/Usage.php for example).
  • generates concrete classes from generic classes with unique names based on name and arguments of generic class.
  • replaces generic class names to concrete class names in places of use.

In this case should be generated:

  • 2 concrete classes of generics BoxForInt and BoxForString;
  • 1 concrete class Usage with replaced generics class names to concrete class names.

Generate vendor/autoload.php with composer dump-autoload command

composer dump-autoload

Run bin/test.php script

php bin/test.php

Composer autoload first checks the "cache" directory and then the "src" directory to load the classes.

Features

What syntax is used?

The RFC does not define a specific syntax so i took this one implemented by Nikita Popov

Syntax example:

<?php

namespace App;

class Generic<in T: Iface = int, out V: Iface = string> {
  
   public function test(T $var): V {
  
   }
}

Syntax problems

I had to upgrade nikic/php-parser for parse code with new syntax.
You can see here the grammar changes that had to be made for support generics.

Parser use PHP implementation of YACC.
The YACC(LALR) algorithm and current PHP syntax make it impossible to describe the full syntax of generics due to collisions.

Collision example:

<?php

const FOO = 'FOO';
const BAR = 'BAR';

var_dump(new \DateTime<FOO,BAR>('now')); // is it generic?
var_dump( (new \DateTime < FOO) , ( BAR > 'now') ); // no, it is not

Solution options

Therefore, nested generics are not currently supported.

<?php

namespace App;

class Usage {
   public function run() {
       $map = new Map<Key<int>, Value<string>>();//not supported
   }
}

Parameter names have not special restrictions

<?php

namespace App;

class GenericClass<T, varType, myCoolLongParaterName> {
   private T $var1;
   private varType $var2;
   private myCoolLongParaterName $var3;   
}

Several generic parameters support

<?php

namespace App;

class Map<keyType, valueType> {
  
   private array $map;
  
   public function set(keyType $key, valueType $value): void {
       $this->map[$key] = $value;
   }
  
   public function get(keyType $key): ?valueType {
       return $this->map[$key] ?? null;
   }
}

Default generic parameter support

<?php

namespace App;

class Map<keyType = string, valueType = int> {
  
   private array $map = [];
  
   public function set(keyType $key, valueType $value): void {
       $this->map[$key] = $value;
   }
  
   public function get(keyType $key): ?valueType {
       return $this->map[$key] ?? null;
   }
}
<?php

namespace App;

class Usage {
   public function run() {
       $map = new Map<>();//be sure to add "<>"
       $map->set('key', 1);
       var_dump($map->get('key'));
   }
}

Where in class can generics be used?

  • extends
  • implements
  • trait use
  • property type
  • method argument type
  • method return type
  • instanceof
  • new
  • class constants

An example of class that uses generics:

<?php

namespace App;

use App\Entity\Cat;
use App\Entity\Bird;
use App\Entity\Dog;

class Test extends GenericClass<Cat> implements GenericInterface<Bird> {
 
  use GenericTrait<Dog>;
 
  private GenericClass<int>|GenericClass<Dog> $var;
 
  public function test(GenericInterface<int>|GenericInterface<Dog> $var): GenericClass<string>|GenericClass<Bird> {
      
       var_dump($var instanceof GenericInterface<int>);
      
       var_dump(new GenericClass<int>::class);
      
       var_dump(new GenericClass<array>::CONSTANT);
      
       return new GenericClass<float>();
  }
}

Where in generic class can parameters be used?

  • extends
  • implements
  • trait use
  • property type
  • method argument type
  • method return type
  • instanceof
  • new
  • class constants

And example of generic class:

<?php

namespace App;

class Test<T,V> extends GenericClass<T> implements GenericInterface<V> {
 
  use GenericTrait<T>;
  use T;
 
  private T|GenericClass<V> $var;
 
  public function test(T|GenericInterface<V> $var): T|GenericClass<V> {
      
       var_dump($var instanceof GenericInterface<V>);
      
       var_dump($var instanceof T);
      
       var_dump(new GenericClass<T>::class);
      
       var_dump(T::class);
      
       var_dump(new GenericClass<T>::CONSTANT);
      
       var_dump(T::CONSTANT);
      
       $obj1 = new T();
       $obj2 = new GenericClass<V>();
      
       return $obj2;
  }
}

How fast is it?

All concrete classes are pre-generated and can be cached(should not affect performance).

Generating many concrete classes should negatively impact performance when:

  • resolves concrete classes;
  • storing concrete classes in memory;
  • type checking for each concrete class.

I think it's all individual for a specific case.

Doesn't work without composer autoload

Autoload magic of concrete classes works with composer autoload only.
Nothing will work because of syntax error if you include file by "require"

Reflection

PHP does type checks in runtime.
Therefore, all generics arguments must me available through reflection in runtime.
It can't be, because information about generics arguments is erased after concrete classes are generated.

Tests

How to run tests?

php bin/test.php

How to add test?

  • Add directory 00-your-dir-name to ./tests
  • Generate output files and check it
php bin/generate.php tests/000-your-dir-name/input tests/000-your-dir-name/output 'Test\'
You might also like...
NFC Reader with the libnfc written in PHP
NFC Reader with the libnfc written in PHP

What is libnfc-for-php? This library is a toy for me. You can read NFC with libnfc written in PHP. Requirements PHP 7.4 or later PHP FFI libnfc Suppor

NFC Reader written in PHP
NFC Reader written in PHP

What is nfc-for-php? This library is a toy for me. You can read NFC with libnfc written in PHP. Requirements PHP 7.4+ PHP FFI libnfc 1.8.0 (if you use

A console noughts and crosses game written in php

Tic-tac-toe A console noughts and crosses game written in php To play, simply clone the file Navigate to the file directory on your terminal and run t

This is a plugin written in PHP programming language and running on the PocketMine platform that works stably on the API 3.25.0 platform

This is a plugin written in PHP programming language and running on the PocketMine platform that works stably on the API 3.25.0 platform. It allows you to hear the sound

Backend repository of libreoupas project, fully written in PHP, using the datas provided by the University.

libreoupas-engine/fst-nancy Description libreoupas est un site Internet permettant aux étudiant de la Faculté des Strasbourg illkirsh d'avoir accès au

Matplotlib. Pyplot written in PHP. Graphs charts, scatter, plot, lines, custom graphs
Matplotlib. Pyplot written in PHP. Graphs charts, scatter, plot, lines, custom graphs

php-libplot. Written in PHP. Graphs charts, scatter, plot, lines, custom graphs Matplotlib. Pyplot written in PHP. Graphs charts, scatter, plot, lines

Minimalistic bookmark manager for your own server written in PHP. Bookmarks are stored in a sqlite database.

b - Bookmark manager b is a minimalistic bookmark manager for your own server. Written in PHP. Bookmarks are stored in a sqlite database. Features: fi

An open-source Minecraft: Java Edition server implementation, written in PHP.
An open-source Minecraft: Java Edition server implementation, written in PHP.

PHPCraft An open-source Minecraft: Java Edition server implementation, written in PHP. What is PHPCraft? PHPCraft is an open-source Minecraft: Java Ed

phly-mustache is a Mustache implementation written for PHP

phly-mustache is a Mustache implementation written for PHP. It conforms to the principles of mustache, and allows for extension of the format via pragmas.

Owner
Anton Sukhachev
Anton Sukhachev
A sampling profiler for PHP written in PHP, which reads information about running PHP VM from outside of the process.

Reli Reli is a sampling profiler (or a VM state inspector) written in PHP. It can read information about running PHP script from outside of the proces

null 272 Dec 22, 2022
A sampling profiler for PHP written in PHP, which reads information about running PHP VM from outside of the process.

Reli Reli is a sampling profiler (or a VM state inspector) written in PHP. It can read information about running PHP script from outside of the proces

null 258 Sep 15, 2022
A multithreaded application server for PHP, written in PHP.

appserver.io, a PHP application server This is the main repository for the appserver.io project. What is appserver.io appserver.io is a multithreaded

appserver.io 951 Dec 25, 2022
A status monitor for Elite Dangerous, written in PHP. Designed for 1080p screens in the four-panel-view in panel.php, and for 7 inch screens with a resolution of 1024x600 connected to a Raspberry Pi.

EDStatusPanel A status monitor for Elite Dangerous, written in PHP. Designed for 1080p screens in the four-panel-view in panel.php, and for 7 inch scr

marcus-s 24 Oct 4, 2022
A PHP parser written in PHP

PHP Parser This is a PHP 5.2 to PHP 8.1 parser written in PHP. Its purpose is to simplify static code analysis and manipulation. Documentation for ver

Nikita Popov 15.9k Jan 8, 2023
A beautiful, fully open-source, tunneling service - written in pure PHP

Expose A completely open-source ngrok alternative - written in pure PHP. Documentation For installation instructions, in-depth usage and deployment de

Beyond Code 3.9k Jan 7, 2023
Websocket chat room written in PHP based on workerman.

基于workerman的GatewayWorker框架开发的一款高性能支持分布式部署的聊天室系统。

walkor 1.1k Jan 8, 2023
A bot written in PHP which attempts to link IRC with SQL database, allowing for integration between platforms

Valeyard IRC-SQL-GateWay A bot written in PHP which attempts to link IRC with SQL database, allowing for integration between platforms. This bot is mo

Valerie Pond 10 Oct 6, 2022
Wake PC is a super tiny password protected webapp for linux machines that sends WOL packets, written in PHP.

Wake PC Wake PC is a super tiny password protected webapp for linux machines that sends WOL packets, written in PHP. How to set up Quick setup You can

Dániel Szabó 45 Dec 30, 2022
A GETTR.com client library written in PHP with Laravel support.

Gettr API Clinet PHP A GETTR.com client library written in PHP with Laravel support. This library uses unofficial publicly accessible API endpoints of

null 10 Dec 13, 2022