Download the PHP package affinity4/magic without Composer

On this page you can find all versions of the php package affinity4/magic. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package magic

Affinity4 Magic

Magic Trait used to easily add event listeners, spelling suggestions in errors and Javascript set and get style setters an getters to any class. Magic!

See the Wiki for this repo for full documentation

Installation

Event Listeners

Simply include Magic in any class to instantly have event listeners!

Once you've included Magic as a trait you can then add any public "camelCased" property starting with "on". You now have an event listener! That's all it takes!

Let's say we have a Model called User

When a new user is registered, we want to email them to let them know their login details.

We'll add the Magic trait and create a public onRegistration property. It must be an array.

Now each time User::register() is called the User::onRegistration() method will also be called, with the users details available to any event listener attached.

Event Listeners

To attach an event listener you simply need to add a callback to the onRegistration array. They will then be called in order every time User::registration() is executed.

Of course you'll want to do something more clever (and security conscious) than this but you get the idea.

"Chained" or "nested" events

IMPORTANT

One thing to always be conscious of is that event listeners are not shared across all instances of the class. If you create the following:

No log event will be fired. This is because the events listener that will log the email is only listening to $EmailA.

This might be fairly obvious when side-by-side like this but in a large project this can be confusing if you forget what instance you are dealing with and what events are bound to it. You could get your logs mixed up, or worse. SO BE CAREFUL!

Containers for Scalability

This is where ServiceManagers, or IoC and DI Containers, are a life saver. However, because Containers will by default always return the same instance of the class when you get it from the container, you will need to use factories if you intend to set your events in the container while creating the class.

However, sometimes it's very powerful to have events unique to each instance. For games with multiple instances of a "Player" class, you don't want every player getting points for a kill do you?

You'll see an example of this in the "Magic Setters and Getters" section

Magic Properties

Another enhancement the Magic trait gives you is the ability to ensure setter and getter methods are called every time you set or get a value directly from a property outside of it's defining class, whether you use the setter/getter methods or not.

Consider this academic example of a user account on a platform like StackOverflow. You have an account with reputation points. There is an event to be called once the user gets to the next "level" and gains access to new features, and so other events can be fired, like emailing them or moderators etc.

IMPORTANT: There is a big mistake here! The mistake here is that the $reputation property has been set to public, allowing the events to be bypassed by mistake.

Let's take a look at an example of this mistake.

Note the lack of @property docblock attributes on the UserAccount class, and that $reputation and $level are both public:

NOTE: You can set it to 9 to verify the level up event doesn't happen if you want.

This is all well and good while things are used as expected, however, because the reputation property and the level property are left as public, the following can be done:

Nothing happens. You could even directly set the level property and nothing would happen. The system is unaware these properties changed.

Magic can fix this just by changing the properties to protected or private and adding 2 doc block attributes!

now this...

...will fire our setter events correctly.

You can still use you're setters and getters as normal of course! But if you forget to, Magic will happen and keep your system working as expected.

Highlander game example

To show how all this can save you tons of conditional if/else/elseif code that becomes a nightmare to maintain, check out this game (or the start of one at least), based on 1986 movie The Highlander. You know, "There can be only one" and all that.

Requirements:

  1. There must be a Highlander class that all players are an instance of
  2. Each player starts the game with a "lifeforce" (not health related) of 10
  3. When a player kills another player they absorb/gain that opponents lifeforce, whatever it may be at the time
  4. We will be aware of how many highlanders are left only when we've killed another player
  5. If there are still other players to defeat the player will shout "There can be only one!"

That's basically the plot of the movie :)

So, first we create a class called Highlander that uses Affinity4\Magic\Magic with 2 private properties $number_of_highlanders and $lifeforce. These will have setter/getter methods set/get_number_of_highlanders and set/getLifeforce. We'll add @property docblock attributes for $number_of_highlanders and $lifeforce to enable the magic. We'll also have a shout method that just echoes a phrase

Next, we create the kills method, which takes in the instance of the player you killed (so you can take their lifeforce etc). It fires the onKill event with the defeated player passed in:

Not only is this less than 75 lines, but no method in the Highlander class has more than 1 line of code! And it will never need to. From now on if we decide we need more to happen when someone gets killed or makes a kill we just add more event handlers!

If that's not magic I don't what is!

Invokable Classes as Event Handlers

While callbacks as event handlers are convenient and quick to write, they have limitations and can often encourage bad design choices.

For example, our Highlander game example in the Magic Properties page, which used callbacks as event handler only had about 11 lines of code. However, it already has serious problems that will only get worse as more lines are added or more callbacks are added.

This is the event handler:

Problem 1: Enforcing Types

Let's start with the first line:

The issue here is that we cannot enforce types. We can type hint $Opponent in our Highlander::kill() method that fires the onKill method, but that assumes we're passing the same value through to the kill method. We may in fact be passing it a generated value, that could be anything.

We're also unable to ensure $Highlander is actually an instance of \Highlander. If something else gets passed in we'll either get errors or worse, we could pass in another class with the same properties and methods that does completely unexpected things. This would mean no errors, but quite possibly hard to debug side-effects.

Problem 2: Single Responsibility

With only a callback to add our code to, we lose the organisational benefits of OOP. It's quite easy to end up breaking SRP without even realizing, especially on projects with numerous developers.

While out code looks initially like it all belongs together, with some closer examination, we can see it's actually modifying 2 parts of our Highlander class, updating $lifeforce and updating $number_of_highlanders

The first 2 lines are only dealing with the $lifeforce property, and should be moved out of this function. However, splitting everything out into their own callback would quickly become messy and hard to maintain. Callbacks and closures would require reading the code to determine what they are doing. If these lines were instead refactored to a class we would know what each class is for and what each method should be doing from the names (which should be clear and descriptive). We would also have everything else classes provide which callbacks do not.

Problem 3: Organization

How should we organize all of this? Should we simply create a separate file for each event in the application and dump everything in each file? We could, but I can imagine that becoming pretty horrible after a while.

Instead, if we had autoloading and a sensible folder structure we could simply loop over autoloaded classes and add event listeners to events. This would mean creating a new class in the right folder would be all it would take to bind a handler to an event.

Solution

Invokable classes can solve all of these problems, and give a few more perks that only OOP can provide. So let's refactor our existing code in to 2 separate event handler classes LifeforceEventHandler and NumberOfHighlandersEventHandler.

TakeOpponentsLifeforceEventHandler

The only requirement of an invokable event handler class is the it has an __invoke() method with the same arguments as out callback. However, we can now do more "setup" using the constructor as well.

Our event handler would now look something like:

We can now now enforce our $Highlander and $Opponent arguments are \Highlander instances. Really we should be using an interface here but that's up to you.

It's also quite clear that this classes purpose is to deal with anything to do with taking your opponents lifeforce.

We could even use the Magic trait here and fire an event for other classes to subscribe to. Let's say we need to add a SpecialAbility feature that gives a player a random special ability after they hit 50 lifeforce points. We could simply add an event onFiftyLifeforce in out __invoke() method. Now our special ability class could subscribe to this event to do what it needs to do.

UpdateNumberOfHighlandersEventHandler

It should be pretty obvious how to implement the UpdateNumberOfHighlandersEventHandler but for completeness sake let's see it

Calling the Event Handlers

To attached our event handlers we simply replace the callbacks with the initialized EventHandler class, like so:

Internally, the invoke methods will be used and passed in our the $Opponent instance from the kill() method

TODO


All versions of magic with dependencies

PHP Build Version
Package Version
No informations.
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package affinity4/magic contains the following files

Loading the files please wait ....