spaf/metamagic

PHP MetaMagic implementation

Installs: 3

Dependents: 0

Suggesters: 0

Security: 0

Stars: 1

Watchers: 1

Forks: 0

Open Issues: 0

pkg:composer/spaf/metamagic

0.0.1 2025-10-08 10:26 UTC

This package is auto-updated.

Last update: 2025-11-08 10:45:15 UTC


README

πŸ‡ΊπŸ‡¦ #StandWithUkraine

This is the official Bank of Ukraine link for donations for Ukraine:

https://bank.gov.ua/en/news/all/natsionalniy-bank-vidkriv-spetsrahunok-dlya-zboru-koshtiv-na-potrebi-armiyi

PHP MetaMagic

This project is a pico-framework (tini-tiny library) for few basic purposes:

  1. Make software more flexible and configurable on a development level.
  2. Working with "Attributed" classes, properties, methods, class-const.
    • searching and filtering the attributed members to perform actions on them.
    • Code examples:
  3. Provide a new way to extend functionality that usually use "Magic Methods".
  4. More comfortable development process through dev-markings and architectural analysis by writing utilities that analyse "Attributes" written by Architects and Lead Developers (future project)

MetaMagic is a fully redesigned subproject of PHP SimpUtils.

Note

There are 2 logical types of PHP Attributes:

  1. "Marker Attribute" - The Attributes that are used just for marking parts of code for analysis and notifications. Those Attributes just mark portion of code and might contain only data/params in them, but do not perform any functionality/logic directly. The most of the native PHP 8+ Attributes play a role of a Marker. Some use cases:
    • #[\Deprecated]
    • #[\ReturnTypeWillChange] It does not affect your code-base runtime (except some log messages or exiting runtime in case of violations in some cases).
  2. "Functional Attribute" - The Attributes that contain logic and functionality directly in the Attribute classes. So those are basically real classes that could be used as normal classes beside being Attributes. It affects your code-base runtime.

Why the difference?! - Because from the architectural standpoint "Functional Attribute" increases coupling due to certain logic in them, while the "Marker Attribute" are dependency-free and does not have a direct coupling, and play role within the context/tools/framework that uses them.

Generic usage

There is basically just a couple of major methods for searching code members covered by attributes.

Simple example of searching members:

use spaf\metamagic\abstract\AbstractSpell;
use spaf\metamagic\enums\TargetType;
use spaf\metamagic\MetaMagic;



#[Attribute(Attribute::TARGET_CLASS)]
class MyClassAttr {}

#[Attribute(Attribute::TARGET_ALL)]
class MyCommonAttr {}

#[MyClassAttr]
class MyClass {

	#[MyCommonAttr]
	public $my_field = null;

	#[MyCommonAttr]
	protected function myMethod() {

	}

}

$my_obj = new MyClass();

$filter = function ($spell): AbstractSpell | null {
	// Returning spell will include it into result, returning `null` will skip this spell
	if ($spell->attr instanceof MyClassAttr) {
		return null;
	}
	return $spell;
};

// Generator of `AbstractSpell` objects returned
$generator = MetaMagic::findSpells(
    // Refs param can contain array with objects, class-refs.
    // You also can directly specify a single ref without wrapping it into array
	refs: $my_obj,
	
	// The same about attrs it can be an array of class-refs or a single class-ref to an Attribute
	attrs: [MyClassAttr::class, MyCommonAttr::class],
	
	// The same about types it can be an array of enum values or a single enum value
	types: [TargetType::ClassType, TargetType::FieldType],
	
	// It allows to control whether you want to
	// search attributes with inheritance involved or exact attribute class.
	// is_instance_of is set to true by default - what means that it will consider inheritance
	// (children classes of an attribute as well)
	is_instance_of: true,
	
	// You can perform a custom filtering by providing a callable
	filter: $filter
);

$first_spell = MetaMagic::findSpellOne(
	// Refs param can contain array with objects, class-refs.
	// You also can directly specify a single ref without wrapping it into array
	refs: $my_obj,
	
	// The same about attrs it can be an array of class-refs or a single class-ref to an Attribute
	attrs: [MyClassAttr::class, MyCommonAttr::class],
	
	// The same about types it can be an array of enum values or a single enum value
	types: [TargetType::ClassType, TargetType::FieldType],
	
	// It allows to control whether you want to
	// search attributes with inheritance involved or exact attribute class.
	// is_instance_of is set to true by default - what means that it will consider inheritance
	// (children classes of an attribute as well)
	is_instance_of: true,
	
	// You can perform a custom filtering by providing a callable
	filter: $filter
);

print_r($first_spell);

Important

Filtering by params and especially by filter: $filter param allows to reduce some overhead for not processing every single spell when those are not needed.

This is especially important when you are searching only first occurrence with MetaMagic::findSpellOne()

Output:

spaf\metamagic\spells\SpellField Object
(
    [target] => spaf\metamagic\components\TargetReference Object
        (
            [reflection] => ReflectionProperty Object
                (
                    [name] => my_field
                    [class] => MyClass
                )

            [object] => MyClass Object
                (
                    [my_field] => 
                )

        )

    [attr] => MyCommonAttr Object
        (
        )

)

Magic Methods syntactic sugar

The magic methods are ugly and inherently inconsistent in PHP.

So special MetaMagic attributes implemented for a few reasons:

  1. To demonstrate the power of the MetaMagic pico-framework (purpose of example)
  2. To prettify the class code (it's value is very small, but can improve readability what can cause better maintenance)

Note

There is no much reason to use these attributes besides slightly improving readability and code analysis.

Additional documentation

  1. About spells

License is "MIT"

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Installation

Important

This code is a very minimal prototype. It means that all the planned optimizations are not yet applied. So further improvements of performance should be coming in the future versions

Minimal PHP version: 8.4

Current framework version: 0.0.1