Dev Blog
When using our favorite IDE, we usually have option to find all implementations of interface. This gives us overview of available classes that can do certain operations or have certain purpose. When developing modular application the new types might appear when we install additional dependencies. As well some implementations - through less common - could possibly disappear on dependency upgrade. We have developers tools to to keep our applications up to date to our or external vendor dependencies. But, hey we are developers who do not like to track manually all changes, as it's error prone and tedious. For instance if we have some notification interface and bunch of notifiers. At some point we need to collect those notifiers and notify with them about our application action. The other example might product interface indicating that class represents some kind of product in our system. Then when creating new product, we've had to choose which one from the list of all implementations of product interface. To avoid hardcoding such list of available implementations we could use configuration file to set up all available options. But that's in fact hardcoding configuration, as it would likely be committed with code.
As in real life, often we need to collect some kind of things. Things that have certain purpose.
Collecting Options
  How about if we could collect such classes automatically in our
  application? So that we could have literally array of objects
  implementing particular interface. The PHP does not facilitate
  such feature. First thought might be to use the built-in 
  get_declared_classes PHP function. But it will return
  only classes that are already included. With currently
  widely used class auto-loaders the 
  get_declared_class will fail in our use case.
The brute-force method could be used to obtain list of interfaces - just scan all files and check for implementations - then cache it. That's the way to go, however with many pitfalls. Often it is not even possible to autoload some classes or even include as it will result in fatal error. Such list could be useful for smaller projects.
Using Signals
TLDR/Get repository with example:
git clone https://github.com/MaslosoftGuides/signals.collecting-interfaces.git cd signals.collecting-interfaces composer install php signals.php
  The signals project
  is designed to make free communication with application
  components, somewhat like connecting devices with WI-FI versus
  connecting by wire. So that once configured and in range,
  components will communicate. In other words, once components are
  added and signals definition generated the components can
  communicate. At very first implementations signals could be
  attached to classes only, however newer versions support adding
  signals and slots also to traits and interfaces. Particularly
  interested side feature appeared for interface declared signals.
  Adding @SignalFor
  annotation on top of interface declaration will actually instruct
  gather to
  collect all classes implementing this interface! Then with simple
  method call we can obtain object instances implementing such
  interface. The implementing class might or might not be aware of
  being gathered, as the annotation is on class definition itself,
  so that it does not require any method or property. In fact even
  empty interface can be used. 
  Before staring example, please add signals to our project
  with composer require maslosoft/signals
  command.
  For example let's create our NotifierInterface:
<?php
namespace Maslosoft\Guides\Signals\Interfaces;
use Maslosoft\Addendum\Interfaces\AnnotatedInterface;
use Maslosoft\Guides\Signals\Slots\NotifierSlot;
/**
 * @SignalFor(NotifierSlot)
 * @see NotifierSlot
 */
interface NotifierInterface extends AnnotatedInterface
{
        public function notify($message);
}
  Notice the @see PHP doc block instructing IDE to
  keep use statement for NotifierSlot.
  Notice that to have class processed by annotations
  engine, it must implement AnnotatedInterface.
  Either extend NotifierInterface from
  AnnotatedInterface or let implementing classes
  implement this.
  Having class used as signal, or more like beacon we need also
  receiver. The receiver is used to trigger gathering as parameter
  to gather method. The receiver must implement
  SlotInterface which will receive signal by
  setSignal method when gathering beacon signals. In
  our notifier example, the $signal value will be
  instance of concrete receiver. The receiver can also do something
  with received result before returning it back.
  Example receiver for our NotifierInterface beacon
  signal, this will be used to gather notifiers:
<?php
namespace Maslosoft\Guides\Signals\Slots;
use Maslosoft\Signals\Interfaces\SlotInterface;
use Maslosoft\Signals\ISignal;
class NotifierSlot implements SlotInterface
{
        private $notifier = null;
        public function setSignal(ISignal $signal)
        {
                $this->notifier = $signal;
        }
        public function result()
        {
                return $this->notifier;
        }
}
  The next thing to do is to create some notifiers, by creating
  classes
  implementing NotifierInterface with one
  method notify. In our example this will only echo
  provided message suffixed with class name.
  To make signals
  scanning faster, the signals.yml file can be
  added, specifying which paths to scan:
paths:
- src
Now finally call signals scanning command can be issued:
vendor/bin/signals build
  Bear in mind that if You use composer autoloading and just added
  autoload config part to composer.json, run composer
  dumpautoload first. The signals scanner will show errors
  indicating any problems encountered, however it will try to
  continue scanning. If autoloading is not properly configured in
  our example it will display error message like following:
Error: Could not find class NotifierSlot, when processing annotations on Maslosoft\Guides\Signals\Interfaces\NotifierInterface, near NotifierSlot) * @see NotifierSlot */ while scanning file src/Interfaces/NotifierInterface.php
  When build is complete without errors, there will be - linux
  style - no output. To get some verbose feedback add
  -v up to -vvv parameter (verbose,
  very verbose, very very verbose):
vendor/bin/signals build -vvv
  This command will create runtime and generated
  folders. The runtime content should not be added to
  repository, while generated should be committed
  along with code. The generated folder should contain
  signals-definition.php file containing relations
  between signals and slots.
  To see the results, we've add an application entry file in
  project root folder, let's name it signals.php and
  trigger our notifiers in it. To make it work we need to include
  composer autoload file too:
<?php
use Maslosoft\Guides\Signals\Interfaces\NotifierInterface;
use Maslosoft\Guides\Signals\Slots\NotifierSlot;
use Maslosoft\Signals\Signal;
require 'vendor/autoload.php';
$results = (new Signal)->gather(new NotifierSlot);
/* @var $results NotifierInterface[] */
foreach($results as $notifier)
{
        $notifier->notify('Hello Signals!');
}
  When using repository project, to see the results, run php
  signals.php, You should have output like this:
Email : Hello Signals! Facebook: Hello Signals! Twitter : Hello Signals!
Summary
  While the setup might seem to be complicated, the benefits are
  that adding new notifiers will automatically gather them
  by signals mechanism. This includes those in current project as
  well as additionally added as external libraries. No matter on
  which namespace class is created. The only thing which need to be
  made after adding new components is to
  run vendor/bin/signals build command. Removing
  components is even easier, as signals gathering will skip not
  found classes. In the large projects, using multiple libraries
  using signals is inevitable, as it helps keeping communication
  tight and robust.
You might have noticed, that gather mechanism will create new instances of notifiers. In this example those do not have any configurable properties. But in many projects, objects do require some parameters. The best way to apply configuration is to use dependency injection, or even better embedded dependency injection.
The Signals do not require database and when signals definition is committed along with the code, application is ready to use right away. Signals definitions can be even combined from different sources, but that's a topic for another article.
 
             
		 
																	                                
							 
																		 
																	                                
							 
																		 
																	                                
							 
																		 
																	                                
							 
																		 
																	                                
							 
																		