eboreum/cloner

Immutably clone (copy) objects and override their properties. Works for readonly classes and properties as well.

Maintainers

Package info

gitlab.com/eboreum/cloner

Issues

pkg:composer/eboreum/cloner

Statistics

Installs: 0

Dependents: 0

Suggesters: 1

Stars: 0

0.0.2 2026-06-08 08:09 UTC

This package is auto-updated.

Last update: 2026-06-08 06:26:18 UTC


README

license pipeline status coverage report PHPStan Level

eboreum-cloner

Immutably clone (copy) objects and override their properties. Works for readonly classes and properties as well.

Optionally, add custom logic in a __clone method in your own class to apply regular cloning logic. The logic in the __clone method is executed after values have been copied to a clone.

Requirements

"php": "^8.5",
"eboreum/caster": "^3.0",
"eboreum/immutable-interface": "^1.0"

Installation

Via Composer (https://packagist.org/packages/eboreum/cloner):

composer require eboreum/cloner

Via GitLab:

git clone git@gitlab.com:eboreum/cloner.git

Simple cloning

If all you need is a simple clone wrapper, without the need for changing properties, you may use Eboreum\Cloner\SimpleCloner instead. Using SimpleCloner rather than the native clone operator (conventional Object Cloning; clone $object) can make unit testing easier because the cloner can be mocked or stubbed in PHPUnit. This allows tests to verify that cloning was requested and to control which object is returned, capabilities that are not available when using the clone language construct directly.

Corner cases: PHP internal classes

PHP has a series of internal classes, including DateTimeImmutable and SplFileObject, to name a few. Some of these internal classes break with conventional object state behaviors. Here are three examples:

  1. DateTimeImmutable
    Exposes debugger-visible date/time fields, but its real state is native timelib data: timestamp, timezone object, parsed calendar fields, relative info, etc. If created with ReflectionClass::newInstanceWithoutConstructor(), that native state is missing, so methods like format() can fail with "not correctly initialized”" Reflection explicitly allows constructor bypassing, but internal final classes may reject it.
  2. SplFileObject
    It may show no meaningful PHP properties, but it wraps a native file handle plus cursor state: path, mode, EOF status, current line, flags, CSV controls, seek position, and stream metadata. The manual describes it as an object-oriented interface to a file, not as a property-backed value object.
  3. SimpleXMLElement
    This looks like a PHP object with dynamic-looking child/attribute access, but it is backed by libxml node/document state. print_r()/property access can make it feel property-based, yet its actual identity is a pointer into an XML tree plus namespace/XPath/iterator context. The manual lists methods such as children(), attributes(), xpath(), and iterator methods, showing the state lives in the XML node model rather than normal PHP properties.

So these are "objects" at the PHP surface, but their invariants live in C-level structures. Reflection can sometimes create the shell, but not the internal payload.

This library will, whenever possible, restore the state of these objects. If this is not possible, a Eboreum\Cloner\Exception\CannotRestoreObjectException is thrown.

Realistically, you will only really encounter these corner cases when you have objects that extend the internal PHP classes. Or classes added by some PHP extension, PECL or otherwise, where state information is not stored in class properties.

Alternatives

License

See LICENSE file. Basically: Use this library at your own risk.

Contributing

We prefer that you create an issue and or a merge request at https://gitlab.com/eboreum/cloner, and have a discussion about a feature or bug here.

Credits

Authors

Acknowledgements

This implementation is inspired by https://packagist.org/packages/spatie/php-cloneable. However, there are six main differences to the Spatie implementation.

See: https://github.com/spatie/php-cloneable/blob/8760f6ce285a1016075c583eae764f562e214b6d/src/Cloneable.php

These six differences are:

  1. The "continue" on line 26 seems like a bug in that it silently ignores unmatched keys in the $values argument.
  2. The "bindTo" call on 34 may cause a TypeError. This results in sequential error discovery (one at a time).
  3. In eboreum/cloner (this codebase), we walk parent classes to clone parent-scoped properties as well. Something that spatie/php-cloneable does not do, which can lead to undesired behavior.
  4. In spatie/php-cloneable, dynamic properties are not transferred.
  5. In eboreum/cloner (this codebase), __clone is called when an object defines it.
  6. In eboreum/cloner (this codebase), we will attempt to restore object state for PHP internal classes.

In the implementation in eboreum/cloner (this codebase):

  • Instead of a single TypeError (once per faulty property value), we aggregate all type problems and report all at once.
  • If a child class and a parent class both declare a property with the same name, only the first-discovered property may be overridden from $values; later parent-scoped properties with that name are copied from the original object.
  • Dynamic properties are transferred, which mirrors the behavior of clone $myObject.
  • The __clone method is invoked using a closure bound to the method's declaring class.

AI Implementation Assistance Disclaimer

AI, GPT-5.5 specifically, was used in the implementation of this codebase. However, the code has been thoroughly reviewed by human eyes.