PHP generics written in PHP

Related tags

php-generics
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\'
Releases(1.0.2)
Owner
Anton Sukhachev
Anton Sukhachev
GitHub action to set up PHP with extensions, php.ini configuration, coverage drivers, and various tools.

GitHub action to set up PHP with extensions, php.ini configuration, coverage drivers, and various tools.

Shivam Mathur 1.8k Oct 22, 2021
Easy to use utility functions for everyday PHP projects. This is a port of the Lodash JS library to PHP

Lodash-PHP Lodash-PHP is a port of the Lodash JS library to PHP. It is a set of easy to use utility functions for everyday PHP projects. Lodash-PHP tr

Lodash PHP 439 Oct 9, 2021
Collection pipeline library for PHP

Knapsack Collection pipeline library for PHP Knapsack is a collection library for PHP >= 5.6 that implements most of the sequence operations proposed

Dušan Kasan 523 Oct 12, 2021
🔨 Prefixes all PHP namespaces in a file/directory to isolate the code bundled in PHARs.

PHP-Scoper PHP-Scoper is a tool which essentially moves any body of code, including all dependencies such as vendor directories, to a new and distinct

Humbug 471 Oct 17, 2021
Make your PHP arrays sweet'n'safe

Mess We face a few problems in our PHP projects Illogical type casting (PHP's native implementation is way too "smart") Pointless casts like array =>

Artem Zakirullin 164 Jul 9, 2021
Clean Code concepts adapted for PHP

Clean Code PHP Table of Contents Introduction Variables Use meaningful and pronounceable variable names Use the same vocabulary for the same type of v

Piotr Plenik 9.7k Oct 20, 2021
Cheatsheet for some Php knowledge you will frequently encounter in modern projects.

Cheatsheet for some Php knowledge you will frequently encounter in modern projects.

Arnaud Becher 1 Oct 20, 2021
AMP Optimizer PHP library

AMP Toolbox for PHP A collection of AMP tools making it easier to publish and host AMP pages with PHP. The following tools are part of this library: A

AMP 51 Oct 20, 2021
The easiest way to match data structures like JSON/PlainText/XML against readable patterns. Sandbox:

PHP Matcher Library created for testing all kinds of JSON/XML/TXT/Scalar values against patterns. API: PHPMatcher::match($value = '{"foo": "bar"}', $p

Coduo 684 Oct 13, 2021
PHP Machine Learning library

PHP-ML - Machine Learning library for PHP Fresh approach to Machine Learning in PHP. Algorithms, Cross Validation, Neural Network, Preprocessing, Feat

Jorge Casas 116 Oct 17, 2021
Okex API Like the official document interface, Support for arbitrary extension.

It is recommended that you read the official document first Okex docs https://www.okex.com/docs/en Okex Simulation Test API https://www.okex.com/docs/

lin 24 Oct 9, 2021
PhpGit - A Git wrapper for PHP 7.1+

PhpGit PhpGit - A Git wrapper for PHP 7.1+ The project is forked from https://github.com/kzykhys/PHPGit Requirements PHP 7.1+ Git Installation Update

PHPPkg 6 Oct 12, 2021
Prettier PHP Plugin

Prettier PHP Plugin Intro Prettier is an opinionated code formatter. It enforces a consistent style by parsing your code and re-printing it with its o

Prettier 1.4k Oct 13, 2021
This tool is used to build and maintain browscap files.

Browser Capabilities Project This tool is used to build and maintain browscap files.

Browser Capabilities Project 369 Oct 10, 2021
The Current US Version of PHP-Nuke Evolution Xtreme v3.0.1b-beta often known as Nuke-Evolution Xtreme. This is a hardened version of PHP-Nuke and is secure and safe. We are currently porting Xtreme over to PHP 8.0.3

2021 Nightly Builds Repository PHP-Nuke Evolution Xtreme Developers TheGhost - Ernest Allen Buffington (Lead Developer) SeaBeast08 - Sebastian Scott B

Ernest Buffington 5 May 28, 2021
A PHP library to play with the Raspberry PI's GPIO pins

php-gpio php-gpio is a simple PHP library to play with the Raspberry PI's GPIO pins. It provides simple tools such as reading & writing to pins. [UPDA

Ronan Guilloux 266 Aug 2, 2021
Detect flaws in your architecture, before they drag you down into the depths of dependency hell ...

Detect flaws in your architecture before they drag you down into the depths of dependency hell ... What it does System Requirements Installation Phive

Michael Haeuslmann 477 Sep 27, 2021
PPM is a process manager, supercharger and load balancer for modern PHP applications.

PPM - PHP Process Manager PHP-PM is a process manager, supercharger and load balancer for PHP applications. It's based on ReactPHP and works best with

PPM - PHP Process Manager 6.3k Oct 22, 2021
Humanize values that are readable only for developers.

PHP Humanizer Tests - 4.x Readme for 4.x version Humanize values to make them readable for regular people ;) Installation Run the following command: c

Coduo 1.4k Oct 16, 2021