by Dinesh Thakur

PHP has made a big step forward in object-oriented programming with version 5. Since this version, it allows to implement methods in predefined behavior by PHP. These methods are called "magic methods" __call () is one of them.

Call a method that does not exist

Take the example of a class that models a Penguin, which instantiates then called his method "fly".

<?php
   class Penguin
    { }
    $web = new Penguin ();
    $web -> fly ('Africa');
?>

This piece of code will throw you an error. You do not know but maybe penguins can not fly:

Fatal error: Call to undefined method web::fly () in /path/to/Code-php/magic_methods.php on line 4.

This small reminder morphological allows you to see especially the following: you can not call a method that does not exist. PHP However, thanks to the magic method __call () allows you to violate a basic law of nature, namely to fly a penguin or generally call a method that has not been declared in your class.

Implement the __call () method

The __call () method takes two parameters. The first contains the name of the method that you tried to call, the second contains the arguments you passed him. The following listing shows the overall structure of this method.

<? php
    class MyObject
    {
         / **
         * Method magic __call ()
         *
         *param String $ method name of the method to call
         *param Array $ arguments Table settings
         *return Void
         * /
         public function __call ($method, $arguments)
          {
               // Code to execute custom
          }
    }

?>
Now take the example of Penguin.
<?php
     class Penguin
     {
         / **
         * Method magic __call ()
         *
         *param String $method name of the method to call
         *param Array $arguments Table settings
         *return Void
         * Private access
         * /
         private function __call ($method, $arguments)
         {
         echo 'You called the method', $ method, 'with arguments' implode (',', $ arguments);
         }
    }

      $web = new Penguin ();
      $web -> fly ('Africa');
?>

A few notes:

If you have made your __call () method public, you'll also be able to call directly by doing: $ web -> __ call ('fly', 'Africa'); but there will be a small difference.
By calling the fly () method, the variable $ arguments is an array storing different arguments. Conversely, if you go through the __call () method, the second argument is the type you want.

Case in point: creating a search engine

We will try to recreate a search engine. Notice that we use the PROOF class presented in a previous tutorial, which provides access to the database via the native PDO extension.

<? php
    class SearchEngine
    {
        / **
        * Performs a search in the database
        * Based on criteria supplied argument
        *
        *param Array $ terms Table search criteria
        *return Array $ return results Table
        *see SPDO
        * /
        public function search ($terms = array ())
        {
           $query = "SELECT id FROM table ';

           if (sizeof ($conditions)> 0) {
              $query .= 'WHERE' .implode ('AND', $conditions);
               }

             // Run the SQL statement with PDO Class
             $result = SPDO :: getInstance () -> query ($query);
             $return = $result-> fetchAll (PDO :: FETCH_ASSOC);
             return $return;
        }
   }
?>

As you can see, this search engine has a search () method that takes an array argument to apply different conditions to the application performing the search. These conditions are of the form: fieldname = "value".

You will agree like me (I hope!) That this syntax is more practical, I do not see myself use the query like this:

<?php
     $mySearchEngine = new SearchEngine();
     $mySearchEngine -> search (array (
     'field1' => 'Code-php'
     'field2' => 'Study'
));

It would be really nice to make $ mySearchEngine-> searchByName ('Study'); for example, or $ mySearchEngine-> searchByNameAndDate ('Study', '11/05/1988'); not true ?

And this is where we will be able to implement the __call () method.
<?php
    class SearchEngine
    {
        / **
        * Performs a search in the database
        * Based on criteria supplied argument
        *
        *param Array $ terms Table search criteria
        *return Array $ return results Table
        *see SPDO
        * /
        public function search ($ terms = array ())
          {
             $ query = "SELECT id FROM table ';
               if (sizeof ($ conditions)> 0) {
                  $ query = 'WHERE' implode ('AND', $ conditions);
          }
         // Run the SQL statement with PDO Class
         $ result = SPDO :: getInstance () -> query ($ query);
         $ return = $ result-> fetchAll (PDO :: FETCH_ASSOC);
            return $ return;
     }

      / **
      * Magic method __call () allows to call a virtual method
      * kind of SearchByName (), searchByAge () or searchByNameAndAge () ...
      *
      *param String $ method name of the virtual method called
      *param Array $ args Table search criteria
      *return Array | null $ return results table or NULL
      *see SearchEngine :: search ()
      * /
      public function __call ($ method, $ args)
       {
           if (preg_match ('#^searchBy#i', $method))
              {
                  $ searchConditions = str_replace ('searchBy', '', $method);
                  $ searchCriterias = explode ('and' $ searchConditions);
                  $ terms = array ();
                  $ nbCriterias = sizeof ($ searchCriterias);
                  for ($ i = 0; $ i <$nbCriterias; $ i ++)
                       {
                            $ Conditions [] = strtolower ($ searchCriterias [$ i]) ="'$ args [$ i]'" ';
                       }
                       return $ this-> search ($ conditions);
             }
                       return null;
      }
}
?>

This is a fairly substantial piece of code to digest, so let's dissect it step by step:

• To begin, it is verified that the method we tried to call is a method whose name begins with "searchBy". This step is not necessary, we will call it a care: we make so intuitive code.
• Recovering what is after searchBy in this example: name
• In case we would have several conditions, such searchByNameAndDate are recovered each field test, NameAndDate here.
•For each parameter, the condition is created, in the case of: $ Google-> searchByNameAndDate ('Study","11/05/1988"); we obtain a table with name = "Study" and date = "11/05/1988" that we will be able to pass a parameter to the search () method, as we spoke above.

Disadvantages of the use of the magic __call () method

Just as magic methods __get () and __set (), the magic __call () method has two significant drawbacks when developing professional environnemet. Indeed, using __call () initially prevents the automatic generation of code documentation using the APIs (PHP Documentor for example) using the introspection objects (Reflection). Moreover, it also prevents IDE like Eclipse to introspect the code of the class and thus offer auto-completion of code. A so use sparingly!