You are here

moopapi.module in Module Object Oriented Programming API 7.2

Same filename and directory in other branches
  1. 6.2 moopapi.module
  2. 6 moopapi.module
  3. 7 moopapi.module

File

moopapi.module
View source
<?php

/**
 * Implements hook_boot().
 */
function moopapi_boot() {

  /**
   * This hook is absolutely necessary to force drupal to load this module early on.
   * All contrib oop modules will fail to execute properly if we fail to load this module before them.
   */

  // Register itself.
  module_invoke('moopapi', 'register', 'moopapi');

  // Include Patchwork as early as possible to empower redefining.
  $patchwork_path = DRUPAL_ROOT . '/' . 'sites/all/libraries/patchwork/Patchwork.php';
  if (file_exists($patchwork_path)) {
    include_once $patchwork_path;
  }
}

/**
 * Implements of hook_init().
 */
function moopapi_init() {

  // Fetch all registered classes.
  $classes = moopapi_register();
  foreach ($classes as $app => $decorators) {
    $methods = get_class_methods($app);

    // Casting to array is a protection from disabling dependent module.
    foreach ((array) $methods as $method) {
      moopapi_wrap($app, $method, $decorators);
    }
  }
}

/**
 * Implements hook_libraries_info().
 *
 * @return
 *   An associative array whose keys are internal names of libraries and whose
 *   values are describing each library. Each key is the directory name below
 *   the 'libraries' directory, in which the library may be found.
 */
function moopapi_libraries_info() {
  $libraries['patchwork'] = array(
    'name' => 'Patchwork library',
    'vendor url' => 'http://antecedent.github.com/patchwork',
    'download url' => 'https://github.com/antecedent/patchwork',
    'version arguments' => array(
      'file' => 'README.md',
      'pattern' => '@Version ([0-9\\.]+)@',
      'lines' => 5,
    ),
    'files' => array(
      'php' => array(
        'Patchwork.php',
      ),
    ),
  );
  return $libraries;
}

/**
 * Api function that will create function wrapper to a class method
 *
 * @param string $app
 * @param string $method
 * @param array $decorators
 */
function moopapi_wrap($app, $method, array $decorators = array()) {

  /**
   * We can't pass an array to defined as text function - so use
   * this workaround with setting a variable, which is going to
   * be read during function evaluation.
   * @see moopapi_object()
   */
  global $conf;
  $app_lower = strtolower($app);
  $conf["{$app_lower}_decorators"] = serialize($decorators);
  $function_name = "{$app}_{$method}";
  if (in_array($method, moopapi_hook_ignore_list())) {

    /**
     * I am assuming that developer wants this method to be executed
     * Because developer explicitly implemented this hook both in function and in method.
     *
     * WARNING: we cannot predict the type of arguments to be passed here. so we pass none.
     * This limitation is to the developer to figure out how to bypass
     */
    moopapi_object($app)
      ->{$method}();
    return;
  }
  elseif (function_exists($function_name) && !in_array($function_name, unserialize(variable_get('moopapi_functions_overridden', serialize(array()))))) {

    /**
     * This could mean that developer chose to create function on his own, so we respect his wishes and skip re-implementing it
     * If in this step function does not exist it means that it was not created by the developer or previously by us.
     * lets create it.
     */
    return;

    // do not create it again
  }

  /**
   * In order to enable passing proper parameters (and parameters by reference)
   * we must use PHP Reflection ( Ref: http://us.php.net/language.oop5.reflection )
   * to auto-discover certain properties, in this case number of arguments
   * a method expects.
   */
  $ref_method = new ReflectionMethod($app, $method);
  $parameters = $ref_method
    ->getParameters();

  // Determine whether to create or replace.
  if (Patchwork\Utils\callbackTargetDefined($function_name)) {

    // Use replaceLater as it allows not yet defined functions.
    Patchwork\replaceLater($function_name, function () {

      // To pass additional params that are not declared.
      $full_args = func_get_args();
      foreach ($parameters as $number => $parameter) {
        $var_name = $parameter
          ->getName();
        if ($parameter
          ->isPassedByReference()) {

          // Right part is a reference to a variable, which is called \$var_name.
          $full_args[$number] =& ${$var_name};
        }
      }
      $application = moopapi_object($app);
      return call_user_func_array(array(
        $application,
        $method,
      ), $full_args);
    });
  }
  else {
    $args = moopapi_create_args($parameters);
    $function = <<<END
function {<span class="php-variable">$function_name</span>}({<span class="php-variable">$args</span>}) {
  // To pass additional params that are not declared.
  \$full_args = func_get_args();
  \$ref_method = new ReflectionMethod('{<span class="php-variable">$app</span>}', '{<span class="php-variable">$method</span>}');
  \$parameters = \$ref_method->getParameters();
  foreach (\$parameters as \$number => \$parameter) {
    \$var_name = \$parameter->getName();
    if (\$parameter->isPassedByReference()) {
      // Right part is a reference to a variable, which is called \$var_name.
      \$full_args[\$number] = &\$\$var_name;
    }
  }
  \$application = moopapi_object('{<span class="php-variable">$app</span>}');
  return call_user_func_array(array(\$application, '{<span class="php-variable">$method</span>}'), \$full_args);
}
END;

    // This is what makes the magic possible create function in runtime that calls
    // our objects.
    eval($function);
  }
}

/**
 * API to create arguments' string.
 *
 * @param array $parameters
 * @param boolean $clean
 * @return arg_string
 */
function moopapi_create_args($parameters, $clean = FALSE) {
  $args = array();
  foreach ($parameters as $i => $parameter) {
    $prefix = '';
    if (!$clean) {
      $prefix = $parameter
        ->isPassedByReference() ? '&' : '';
    }
    $name = $parameter
      ->getName();
    $arg_definition = "{$prefix}\${$name}";
    if ($parameter
      ->isOptional()) {
      $default_value = $parameter
        ->getDefaultValue();
      $default_value = $default_value ? $default_value : 'NULL';
      $arg_definition .= "={$default_value}";
    }
    $args[$i] = $arg_definition;
  }
  $args = implode(', ', $args);
  return $args;
}

/**
 * Central pool of all of existent and loaded oop module objects.
 */
function moopapi_object($app) {
  static $objects = array();
  $app_lower = strtolower($app);
  $new_object = ComponentFactory::get($app, Component::TYPE_CONTROLLER, Component::ID_APPLICATION, unserialize(variable_get("{$app_lower}_decorators", serialize(array()))));

  // @todo Implement caching per decorator.
  if (!isset($objects[$app]) || get_class($objects[$app]) != get_class($new_object)) {
    $objects[$app] = $new_object;
  }
  return $objects[$app];
}

/**
 * OOP modules must register themselves before they can be initialized
 * Modules can use this API function during boot and init hooks to register themselves so moopapi
 * can integrate them with the framework.
 *
 * @staticvar array $classes
 * @param string $app
 * @param array $decorators
 * @return array
 */
function moopapi_register($app = NULL, array $decorators = array()) {
  static $classes = array();
  if ($app !== NULL && !isset($classes[$app])) {

    // Unify all classnames as follows: Application, Foo, Bar,...
    $classes[ucfirst(strtolower($app))] = array_filter($decorators, 'ucfirst');
  }
  return $classes;
}

/**
 * Return list of hooks which must not be created as wrappers.
 */
function moopapi_hook_ignore_list() {
  return array(
    'boot',
  );
}
function moopapi_get_major_version() {
  static $moopapi_major_version;
  if (empty($moopapi_major_version)) {

    // Workaround to deal with unavailability of VERSION during bootstrap.
    // @see http://drupal.org/node/618938
    $moopapi_major_version = defined('VERSION') ? current(explode('.', VERSION)) : '6';
  }
  return $moopapi_major_version;
}

Functions

Namesort descending Description
moopapi_boot Implements hook_boot().
moopapi_create_args API to create arguments' string.
moopapi_get_major_version
moopapi_hook_ignore_list Return list of hooks which must not be created as wrappers.
moopapi_init Implements of hook_init().
moopapi_libraries_info Implements hook_libraries_info().
moopapi_object Central pool of all of existent and loaded oop module objects.
moopapi_register OOP modules must register themselves before they can be initialized Modules can use this API function during boot and init hooks to register themselves so moopapi can integrate them with the framework.
moopapi_wrap Api function that will create function wrapper to a class method