2
\$\begingroup\$

I'm writing a small PHP framework to help me develop Web applications. The framework is split into numerous components, each of which are automatically loaded according to the configuration settings. This is my component base class:

class Component {
    public static void init() {}
}

When I create new components, I extend them from Component. For example, I have an Authenticator component that handles user login, logout and some other related functions. Here's an example of the code:

class Authenticator extends Component {
     public static void login($u, $p) {}
     public static void logout() {}
     ...
}

As you can see my methods are static, so when it comes time to use the component, I can easily call the component's functions using Authenticator::METHOD() in the scope of my controller class. There is no need to create an instance of the components since they are in a static context.

But should I make them more OO (eg: make the methods non-static) using DI or some other means? What would be the advantages of this? And considering how I have several components, how would I do this without making things overly complex?

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

The OO equivalent of this is one of:

interface Component {
   public function init();
}

class Authenticator implements Component {
}

// OR

abstract class Component {
   abstract public function init();
}

class Authenticator extends Component {
}

Yes, you will have to create objects without using static. Its really not so bad though.

What are the benefits? I created my own framework that is pure OO. So here is an example of what OO code looks like with dependency injection. I'll describe what it does afterwards so that you get an idea of some of the benefits.

<?php
class Page_XML_Site_Locations extends Page_XML_Site
{
   public function contentMain()
   {
      $model = $this->app->getModelDB(
         'Model_DB_Joint',
         array('Table_Name'       => 'Location',
               'Table_References' => $this->app->getTableReferences(
                  array('References' => array(
                           'Image_List_ID' => array(
                              'Child_Field' => 'List_ID',
                              'References'  => array(
                                 'Image_ID' => array(
                                    'Child_Field' => 'ID',
                                    'Table_Name'  => 'Image')),
                              'Table_Name'  => 'Image_List'),
                           'Map_ID'        => array(
                              'Child_Field'  => 'Map_ID',
                              'Table_Name'   => 'Map')),
                        'Table_Name' => 'Location'))));

      $processing = $this->app->getProcessingNone();
      $settings = $this->app->getSettings();

      $data = $this->app->getData(
         array('Image_List_ID' => $this->app->getNew(
                  'Data_Image_List',
                  array('Dir'         => $settings['Web']['DB_Storage'] .
                        'img/location/',
                        'Empty_Image' => $settings['Web']['No_Photo'],
                        'Formats'     => array('W_Min'),
                        'Image'       => $this->app->getShared('Image'),
                        'Image_Sizes' => $this->app->getShared('Image_Sizes'),
                        'References' => array(
                           'Image_ID' => $this->app->getData()),
                        'Translator'  => $this->tr)),
               'Map_ID'        => $this->app->getNew(
                  'Data_Map',
                  array('References' => array(),
                        'Sensor'     => 'false',
                        'Size'       => '320x320'))));

      $view = $this->app->getView('View_XML_Locations',
                                  array('Data'         => $data,
                                        'Image_Format' => 'W_Min'));

      $controller = $this->app->getController();
      $controller->execute();
   }
}
// EOF

$this->app is my application container responsible for creating and retrieving resources. It is a dependency injection container. There are no calls to new or evil getInstance singletons. All of the objects are managed via the container.

Distinct objects are created (model, processing, settings, data, view, controller). They stand alone (except data which is only created for injection into the view). I don't have to worry about their interface to each other. It is all handled by my event manager and controller. The objects each handle their own area. The Model gets joint data from its referenced tables. The processing handles any POST or GET requests. Everything does its job (which produces a list of stores in the correct language with their details, images and maps for finding them). There is nothing complex to worry about with who is calling who.

That is the top level view. What did I have to do to complete that page?

  • Model, Processing, Settings, Controller - Nothing, that is straight from the framework.
  • Data_Image_List - calculate the image file name from the DB table and constructor settings.
  • Data_Map - calculate the google static map string from the DB table and constructor settings.
  • View - This is where most of the work goes. Loop through the data, writing the elements.

If you don't like the look of the code that I use with my framework then you should probably ignore my suggestions.

What will your Authenticator component do? How tightly will it be coupled to the rest of your system? Can it stand alone allowing you to kepp all your logic for each part of your system distinct?

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.