Usando __call() en PHP 7

La sobrecarga es un término familiar en OOP. Sin embargo, no todos los lenguajes de programación lo interpretan de la misma manera. La noción PHP de sobrecarga es bastante diferente a la de otros lenguajes OO. Donde la sobrecarga tradicional proporciona la capacidad de tener múltiples métodos con el mismo nombre pero con diferentes argumentos, en PHP la sobrecarga significa crear dinámicamente métodos y propiedades.

El desafortunado mal uso del término sobrecarga agrega una capa de confusión para algunos desarrolladores, ya que el término más apropiado para este tipo de funcionalidad podría haber sido ganchos de interpretación.

Hay dos métodos mágicos en la sobrecarga de métodos de soporte de PHP: __call() y __callStatic(). A lo largo de esta sección, veremos más de cerca el método __call().
El método mágico __call() se activa al invocar métodos inaccesibles en un contexto de objeto. Este método acepta dos parámetros, según la siguiente sinopsis:

public mixed __call(string $name, array $arguments)

Sin embargo, los parámetros del método __call() tienen el siguiente significado:

  • $name: este es el nombre del método que se llama
  • $arguments: esta es una matriz enumerada que contiene los parámetros pasados al método $name

El siguiente ejemplo demuestra el uso del método __call() en el contexto del objeto:

<?php
class User
{
public function __call($name, $arguments)
{
echo $name . ': ' . implode(', ', $arguments) . PHP_EOL;
}
public function bonus($amount)
{
echo 'bonus: ' . $amount . PHP_EOL;
}
}
$user = new User();
$user->hello('John', 34);
$user->bonus(560.00);
$user->salary(4200.00);

La clase User misma declaró solo los métodos __call() y bonus(). El objeto $user intenta llamar a los métodos hello(), bonus() y salary(). Esto significa efectivamente que el objeto está tratando de llamar a dos métodos faltantes: hello() y salary(). El método __call() inicia los dos métodos que faltan, produciendo así el siguiente resultado:

__call => hello: John, 34
bonus: 560
__call => salary: 4200

Podemos encontrar un buen ejemplo de caso de uso del método __call() en la plataforma Magento, según la siguiente entrada tomada del archivo de clase vendor/magento/framework/DataObject.php:

public function __call($method, $args)
{
switch (substr($method, 0, 3)) {
case 'get':
$key = $this->_underscore(substr($method, 3));
$index = isset($args[0]) ? $args[0] : null;
return $this->getData($key, $index);
case 'set':
$key = $this->_underscore(substr($method, 3));
$value = isset($args[0]) ? $args[0] : null;
return $this->setData($key, $value);
case 'uns':
$key = $this->_underscore(substr($method, 3));
return $this->unsetData($key);
case 'has':
$key = $this->_underscore(substr($method, 3));
return isset($this->_data[$key]);
}
// …
}

Sin entrar en los detalles del propio Magneto, es suficiente decir que su clase DataObject sirve como un objeto de datos raíz en todo el framework. El código dentro del método __call() le permite mágicamente obtener, establecer, desarmar y verificar la existencia de la propiedad en la instancia del objeto. Esto se usa más adelante en expresiones, como la siguiente entrada tomada del archivo vendor/magento/modulecheckout/Controller/Cart/Configure.php:

$params = new \Magento\Framework\DataObject();
$params->setCategoryId(false);
$params->setConfigureMode(true);
$params->setBuyRequest($quoteItem->getBuyRequest());

El beneficio es que aquí hemos habilitado fácilmente las instancias de DataObject con métodos mágicos que pueden existir o no. Por ejemplo, setCategoryId () es un método que no existe en la clase DataObject. Como no existe, llamarlo activa el método __call(). Esto puede no ser tan obvio al principio, así que consideremos otro ejemplo imaginario donde nuestra clase personalizada se extiende desde DataObject:

<?php
class User extends \Magento\Framework\DataObject
{
}
$user = new User();
$user->setName('John');
$user->setAge(34);
$user->setSalary(4200.00);
echo $user->getName();
echo $user->getAge();
echo $user->getSalary();

Observe la belleza y simplicidad de los setters y getters que hemos logrado aquí con la ayuda del método mágico __call(). Aunque nuestra clase User está básicamente vacía, hemos heredado la magia detrás de la implementación del padre __call().
El método __call() nos brinda algunas posibilidades realmente interesantes, la mayoría de las cuales encajarán como parte de marcos o bibliotecas.

Comparte