{"id":18550276,"url":"https://github.com/xp-forge/partial","last_synced_at":"2025-08-02T00:06:59.088Z","repository":{"id":29710751,"uuid":"33253701","full_name":"xp-forge/partial","owner":"xp-forge","description":"Partials: Compile-time metaprogramming","archived":false,"fork":false,"pushed_at":"2020-10-04T14:51:23.000Z","size":176,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-15T18:56:08.349Z","etag":null,"topics":["accessor","lombok","php","trait","xp-framework"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xp-forge.png","metadata":{"files":{"readme":"README.md","changelog":"ChangeLog.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-04-01T14:53:24.000Z","updated_at":"2020-10-04T14:51:26.000Z","dependencies_parsed_at":"2022-09-06T21:41:42.250Z","dependency_job_id":null,"html_url":"https://github.com/xp-forge/partial","commit_stats":null,"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/xp-forge/partial","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xp-forge%2Fpartial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xp-forge%2Fpartial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xp-forge%2Fpartial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xp-forge%2Fpartial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xp-forge","download_url":"https://codeload.github.com/xp-forge/partial/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xp-forge%2Fpartial/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268315928,"owners_count":24231059,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-01T02:00:08.611Z","response_time":67,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["accessor","lombok","php","trait","xp-framework"],"created_at":"2024-11-06T21:04:04.273Z","updated_at":"2025-08-02T00:06:58.791Z","avatar_url":"https://github.com/xp-forge.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"Partials: Compile-time metaprogramming\n======================================\n\n[![Build Status on TravisCI](https://secure.travis-ci.org/xp-forge/partial.svg)](http://travis-ci.org/xp-forge/partial)\n[![XP Framework Module](https://raw.githubusercontent.com/xp-framework/web/master/static/xp-framework-badge.png)](https://github.com/xp-framework/core)\n[![BSD Licence](https://raw.githubusercontent.com/xp-framework/web/master/static/licence-bsd.png)](https://github.com/xp-framework/core/blob/master/LICENCE.md)\n[![Requires PHP 7.0+](https://raw.githubusercontent.com/xp-framework/web/master/static/php-7_0plus.png)](http://php.net/)\n[![Latest Stable Version](https://poser.pugx.org/xp-forge/partial/version.png)](https://packagist.org/packages/xp-forge/partial)\n\nFor situations where more logic than just \"compiler-assisted copy\u0026paste\" using [PHP's traits](http://php.net/traits) is necessary, this library provides a syntax that expand dynamically based on the containing class at compile time.\n\nPartial flavors\n---------------\nThe partials provided by this library's are divided in two flavors: Kinds and composeables.\n\n* **Kinds** define the general concept of a type. You can say, e.g.: This type ***is*** a list of something, or a reference to something. Or, to use more concrete examples: The `Customers` class is a list of customers (encapsulated by `Customer` instances), and `Name` is a reference to (a string) containing a name.\n* **Composeables** can be used alone or in combination to extend a base type or a kind. You can say, e.g. This type comes ***with*** a certain functionality. Again, using a realistic use-case: The `Person` class comes with `toString()`, `compareTo()` and `hashCode()` methods.\n\nRegardless of their flavor, some partials are actually implemented by a regular PHP trait, others are dynamically created at runtime. However, the syntax for both is `use [Containing-Type]\\[is-or-with]\\[Partial-Name]`.\n\nWalk-through\n------------\nThe `Box` trait creates a value object wrapping around exactly one member. It creates a one-arg constructor, and a `value()` for retrieving the value, and includes appropriate `hashCode()`, `compareTo()` and `toString()` implementations. \n\nWriting this:\n```php\nnamespace example;\n\nuse lang\\partial\\Box;\nuse lang\\Value;\n\nclass Name implements Value {\n  use Name\\is\\Box;\n\n  public function personal() { return '~' === $this-\u003evalue{0}; }\n}\n```\n...is equivalent to:\n```php\nnamespace example;\n\nuse lang\\Value;\n\nclass Name implements Value {\n  protected $value;\n\n  public function __construct($value) { $this-\u003evalue= $value; }\n\n  public function value() { return $this-\u003evalue; }\n\n  public function personal() { return '~' === $this-\u003evalue{0}; }\n\n  public function hashCode() { /* ... */ }\n\n  public function compareTo($value) { /* ... */ }\n\n  public function toString() { /* ... */ }\n}\n```\nThe parametrized `Accessors` trait creates accessors for all instance members. \n\n* * * \n\nWriting this:\n```php\nnamespace example;\n\nuse lang\\partial\\Accessors;\n\nclass Wall {\n  use Wall\\with\\Accessors;\n\n  private $name, $type, $posts;\n\n  public function __construct(\n    Name $name,\n    Type $type,\n    Posts $posts\n  ) {\n    $this-\u003ename= $name;\n    $this-\u003etype= $type;\n    $this-\u003eposts= $posts;\n  }\n}\n```\n...is equivalent to:\n```php\nnamespace example;\n\nclass Wall {\n  private $name, $type, $posts;\n\n  public function __construct(\n    Name $name,\n    Type $type,\n    Posts $posts\n  ) {\n    $this-\u003ename= $name;\n    $this-\u003etype= $type;\n    $this-\u003eposts= $posts;\n  }\n\n  public function name() { return $this-\u003ename; }\n\n  public function type() { return $this-\u003etype; }\n\n  public function posts() { return $this-\u003eposts; }\n}\n```\n\nIf the constructor consists solely of assignments, you can include the `Constructor` trait and remove it. The parameters will be declared in the order the fields are declared: top to bottom, left to right in the source code.\n\n* * * \n\nWriting this:\n```php\nnamespace example;\n\nuse lang\\partial\\Constructor;\n\nclass Author {\n  use Author\\with\\Constructor;\n\n  private $handle, $name;\n}\n```\n...is equivalent to:\n```php\nnamespace example;\n\nclass Author {\n  private $handle, $name;\n\n  public function __construct($handle, $name) {\n    $this-\u003ehandle= $handle;\n    $this-\u003ename= $name;\n  }\n}\n```\n\nTo combine all these, you can use the `Value` trait, which a) creates a constructor with all members as parameters, b) accessors for reading these, and c) implements the `hashCode()`, `compareTo()` and `toString()` methods.\n\nThe `ListOf` trait creates a list of elements which can be accessed by their offset, iterated by `foreach`, and offers `equals()` and `toString()` default implementations.\n\n* * * \n\nWriting this:\n```php\nnamespace example;\n\nuse lang\\partial\\ListOf;\n\nclass Posts implements \\lang\\Value, \\IteratorAggregate {\n  use Posts\\is\\ListOf;\n}\n```\n...is equivalent to:\n```php\nnamespace example;\n\nclass Posts implements \\lang\\Value, \\IteratorAggregate {\n  private $backing;\n\n  public function __construct(...$elements) {\n    $this-\u003ebacking= $elements;\n  }\n\n  public function present() { return !empty($this-\u003ebacking); }\n\n  public function size() { return sizeof($this-\u003ebacking); }\n\n  public function at($offset) {\n    if (isset($this-\u003ebacking[$offset])) {\n      return $this-\u003ebacking[$offset];\n    }\n    throw new ElementNotFoundException(…);\n  }\n\n  public function first() {\n    if (empty($this-\u003ebacking)) {\n      throw new ElementNotFoundException(…);\n    }\n    return $this-\u003ebacking[0];\n  }\n\n  public function getIterator() {\n    foreach ($this-\u003ebacking as $element) {\n      yield $element;\n    }\n  }\n\n  public function compareTo($value) { /* ... */ }\n\n  public function toString() { /* ... */ }\n\n  public function hashCode() { /* ... */ }\n}\n```\n\nThe `Builder` trait will add a static `with()` method to your class, generating a fluent interface to create instances. This is especially useful in situation where there are a lot of constructor parameters.\n\nThe `Comparators` trait adds static `by[Member]` methods returning util.Comparator instances for each member. These instances can be combined using *then* (`Post::byDate()-\u003ethen(Post::byAuthor())`) or reversed (`Post::byDate()-\u003ereverse()`).\n\n```php\nnamespace example;\n\nuse lang\\partial\\Accessors;\nuse lang\\partial\\Builder;\nuse lang\\partial\\Comparators;\n\nclass Post {\n  use Wall\\with\\Accessors;\n  use Wall\\with\\Builder;\n  use Wall\\with\\Comparators;\n\n  private $author, $text, $date;\n\n  public function __construct($author, $text, Date $date) {\n    $this-\u003eauthor= $author;\n    $this-\u003etext= $text;\n    $this-\u003edate= $date;\n  }\n}\n```\n\nThe `ListIndexedBy` trait creates a list of elements which can be queried by name, also overloading iteration and creating `equals()` and `toString()` in a sensible manner. The class needs to implement the abstract `index` method and return a string representing the name.\n\n```php\nnamespace example;\n\nuse lang\\partial\\ListIndexedBy;\n\nclass Walls implements \\IteratorAggregate {\n  use Walls\\is\\ListIndexedBy;\n\n  protected function index($wall) { return $wall-\u003ename()-\u003evalue(); }\n}\n```\n\nPutting it all together, we can see the API:\n\n```php\nuse util\\data\\Sequence;\n\n$post= Post::with()-\u003eauthor('Timm')-\u003etext('Hello World!')-\u003edate(Date::now())-\u003ecreate();\n\n$walls= new Walls(\n  new Wall(new Name('one'), Type::$OPEN, new Posts()),\n  new Wall(new Name('two'), Type::$CLOSED, new Posts($post))\n);\n\n$walls-\u003epresent();        // TRUE, list is not empty\n$walls-\u003esize();           // 2\n$walls-\u003eprovides('one');  // TRUE, wall named one found\n$walls-\u003eprovides('zero'); // FALSE, no such wall\n$walls-\u003efirst();          // Wall(name =\u003e Name(\"one\"), type =\u003e OPEN)\n$walls-\u003enamed('two');     // Wall(name =\u003e Name(\"two\"), type =\u003e CLOSED)\n$walls-\u003enamed('three');   // ***ElementNotFoundException\n\nforeach ($walls as $wall) {\n  Console::writeLine('== ', $wall-\u003ename()-\u003evalue(), ' wall (', $wall-\u003etype(), ') ==');\n  Sequence::of($wall-\u003eposts())-\u003esorted(Post::byDate())-\u003eeach(function($post) {\n    Console::writeLine('Written by ', $post-\u003eauthor(), ' on ', $post-\u003edate());\n    Console::writeLine($post-\u003etext());\n    Console::writeLine();\n  });\n}\n```\n\nSee also\n--------\n* http://groovy-lang.org/metaprogramming.html\n* http://projectlombok.org/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxp-forge%2Fpartial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxp-forge%2Fpartial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxp-forge%2Fpartial/lists"}