it-swarm.com.de

Autoloader für Funktionen

Letzte Woche habe ich gelernt, dass Klassen in Ihr Projekt aufgenommen werden können, indem Sie eine __autoload()-Funktion schreiben. Dann habe ich gelernt, dass die Verwendung eines Autoloaders nicht nur eine Technik, sondern auch ein Muster ist.

Jetzt verwende ich den Autoloader in meinem Projekt und fand ihn sehr nützlich. Ich habe mich gefragt, ob es möglich sein könnte, dasselbe mit Funktionen zu tun. Es kann sehr nützlich sein, die rechte PHP -Datei mit den darin enthaltenen Funktionen zu vergessen. 

Ist es also möglich, einen Funktionsautoloader zu erstellen?

50
Shoe

Es gibt keinen Autoloader für Funktionen. Sie haben vier realistische Lösungen:

  1. Alle Funktionen in Namespace-Klassen einbetten (Kontext geeignet). Nehmen wir an, Sie haben eine Funktion namens string_get_letters. Sie können dies einer Klasse namens StringFunctions als statische Funktion hinzufügen. Anstatt string_get_letters() aufzurufen, würden Sie StringFunctions::get_letters() aufrufen. Sie würden dann diese Namespaced-Klassen __autoload.

  2. Laden Sie alle Funktionen vor. Da Sie Klassen verwenden, sollten Sie das nicht viele Funktionen haben, laden Sie sie also einfach vor.

  3. Laden Sie Funktionen, bevor Sie sie verwenden. In jeder Datei require_once die Funktionsdateien, die in dieser Datei verwendet werden sollen. 

  4. Verwenden Sie keine Funktionen an erster Stelle. Wenn Sie OOP - Code entwickeln (der Ihnen anscheinend sowieso vorkommt), sollten Sie wenig oder gar keine Funktionen benötigen. Alles, wofür Sie eine Funktion (oder mehrere Funktionen) benötigen, können Sie auf eine OO - Weise bauen und die Notwendigkeit von Funktionen vermeiden.

Persönlich würde ich entweder 1, 2 oder 4 vorschlagen, abhängig von Ihrem genauen Bedarf und der Qualität und Größe Ihrer Codebase ...

57
ircmaxell

Wenn Sie Composer in Ihrem Projekt verwenden, können Sie dem Autoload-Abschnitt eine Direktive files hinzufügen. 

Dies erzeugt dann tatsächlich ein required_once im Autoloader, aber es fühlt sich an, als würde es sich um echtes Autoloading handeln, da Sie sich nicht darum kümmern müssen.
Es ist zwar nicht faul geladen.

Beispiel aus Assetic :

"autoload": {
        "psr-0": { "Assetic": "src/" },
        "files": [ "src/functions.php" ]
    }
31
ivoba

Ich habe vor einiger Zeit etwas über einen hässlichen Hack gelesen, der fatale Fehler auffing und versuchte, die fehlenden Funktionen einzufügen und auszuführen, aber ich würde definitiv nicht diesen Weg gehen.

Das nächste, was Sie haben, ist die __call()magic method , die eine Art __autoload() für Methoden ist, nicht Funktionen. Es könnte für Ihre Bedürfnisse gut genug sein; wenn Sie es sich leisten können, eine Klasse aufzurufen, und benötigen Sie jede Funktion separat. Seit PHP 5.3.0 haben Sie auch __callStatic().

Ein Beispiel mit __callStatic():

class Test
{
    public function __callStatic($m, $args)
    {
        if (function_exists($m) !== true)
        {
            if (is_file('./path/to/functions/' . $m . '.php') !== true)
            {
                return false;
            }

            require('./path/to/functions/' . $m . '.php');
        }

        return call_user_func_array($m, $args);
    }
}

Test::functionToLoad(1, 2, 3);

Dies würde die functionToLoad()-Funktion aufrufen, die in ./path/to/functions/functionToLoad.php definiert ist.

14
Alix Axel

Nun, wie üblich gibt es dafür eine PECL-Erweiterung:

(über: http://phk.tekwire.net/joomla/support/doc/automap.htm )

Es soll Funktionen sowie Klassen automatisch laden. Was jedoch mit dem aktuellen PHP Interpreter noch nicht funktioniert.

(Eine alternative Option ist übrigens die Erzeugung von Stub-Funktionen, die Gegenstücke mit Namensraum laden und ausführen.)

Das wird gesagt. Das automatische Laden wird nicht allgemein als gute Praxis betrachtet. Dies führt zu übermäßig zerbrochenen Klassenhierarchien und Objektglück. Der eigentliche Grund für das automatische Laden von PHP ist, dass Include- und Abhängigkeits-Managementsysteme Insuling sind.

7
mario
namespace MyNamespace;

class Fn {

    private function __construct() {}
    private function __wakeup() {}
    private function __clone() {}

    public static function __callStatic($fn, $args) {
        if (!function_exists($fn)) {
            $fn = "YOUR_FUNCTIONS_NAMESPACE\\$fn";
            require str_replace('\\', '/', $fn) . '.php';
        }
        return call_user_func_array($fn, $args);
    }

}

Und mit Namespaces können wir Folgendes tun: Fn::myFunc() und spl_autoload_register(). Ich habe diesen Code mit Beispielen verwendet: https://goo.gl/8dMIMj

2
bryanjhv

Ich benutze eine Klasse und __invoke . Die __invoke-Methode wird aufgerufen, wenn ein Skript eine Klasse als Funktion aufruft. Ich mache oft so etwas:

<?php

namespace API\Config;

class Slim {
  function __invoke() {
    return [
      'settings' => [
        'displayErrorDetails' => true,
        'logger' => [
          'name' => 'api',
          'level' => Monolog\Logger\Logger::DEBUG,
          'path' => __DIR__ . '/../../logs/api.log',
        ],
      ]
    ];
  }
}

Ich kann dann wie eine Funktion aufrufen:

$config = API\Config\Slim;
$app = Slim\App($config())
0
BugHunterUK

Funktionen und Konstanten können nicht automatisch geladen werden. Sie können jedoch etwas wie jesseschalken/autoload-generator verwenden, mit dem automatisch erkannt wird, welche Dateien Dinge enthalten, die nicht automatisch geladen werden können, und sie eifrig laden.

0
Jesse

Hier ist ein anderes recht komplexes Beispiel, basierend auf den Vorschlägen in dieser Diskussion .. Der Code ist auch hier zu sehen: lib/btr.php

<?php
/**
 * A class that is used to autoload library functions.
 *
 * If the function btr::some_function_name() is called, this class
 * will convert it into a call to the function
 * 'BTranslator\some_function_name()'. If such a function is not
 * declared then it will try to load these files (in this order):
 *   - fn/some_function_name.php
 *   - fn/some_function.php
 *   - fn/some.php
 *   - fn/some/function_name.php
 *   - fn/some/function.php
 *   - fn/some/function/name.php
 * The first file that is found will be loaded (with require_once()).
 *
 * For the big functions it makes more sense to declare each one of them in a
 * separate file, and for the small functions it makes more sense to declare
 * several of them in the same file (which is named as the common prefix of
 * these files). If there is a big number of functions, it can be more
 * suitable to organize them in subdirectories.
 *
 * See: http://stackoverflow.com/questions/4737199/autoloader-for-functions
 */
class btr {
  /**
   * Make it TRUE to output debug info on '/tmp/btr.log'.
   */
  const DEBUG = FALSE;

  /**
   * The namespace of the functions.
   */
  const NS = 'BTranslator';

  /**
   * Relative directory where the functions are located.
   */
  const FN = 'fn';

  private function __construct() {}
  private function __wakeup() {}
  private function __clone() {}

  /**
   * Return the full name (with namespace) of the function to be called.
   */
  protected static function function_name($function) {
    return self::NS . '\\' . $function;
  }

  /**
   * Return the full path of the file to be loaded (with require_once).
   */
  protected static function file($fname) {
    return dirname(__FILE__) . '/' . self::FN . '/' . $fname . '.php';
  }

  /**
   * If a function does not exist, try to load it from the proper file.
   */
  public static function __callStatic($function, $args) {
    $btr_function = self::function_name($function);
    if (!function_exists($btr_function)) {
      // Try to load the file that contains the function.
      if (!self::load_search_dirs($function) or !function_exists($btr_function)) {
        $dir = dirname(self::file($fname));
        $dir = str_replace(DRUPAL_ROOT, '', $dir);
        throw new Exception("Function $btr_function could not be found on $dir");
      }
    }
    return call_user_func_array($btr_function, $args);
  }

  /**
   * Try to load files from subdirectories
   * (by replacing '_' with '/' in the function name).
   */
  protected static function load_search_dirs($fname) {
    do {
      self::debug($fname);
      if (file_exists(self::file($fname))) {
        require_once(self::file($fname));
        return TRUE;
      }
      if (self::load_search_files($fname)) {
        return TRUE;
      }
      $fname1 = $fname;
      $fname = preg_replace('#_#', '/', $fname, 1);
    } while ($fname != $fname1);

    return FALSE;
  }

  /**
   * Try to load files from different file names
   * (by removing the part after the last undescore in the functin name).
   */
  protected static function load_search_files($fname) {
    $fname1 = $fname;
    $fname = preg_replace('/_[^_]*$/', '', $fname);
    while ($fname != $fname1) {
      self::debug($fname);
      if (file_exists(self::file($fname))) {
        require_once(self::file($fname));
        return TRUE;
      }
      $fname1 = $fname;
      $fname = preg_replace('/_[^_]*$/', '', $fname);
    }

    return FALSE;
  }

  /**
   * Debug the order in which the files are tried to be loaded.
   */
  public static function debug($fname) {
    if (!self::DEBUG) {
      return;
    }
    $file = self::file($fname);
    $file = str_replace(DRUPAL_ROOT, '', $file);
    self::log($file, 'Autoload');
  }

  /**
   * Output the given parameter to a log file (useful for debugging).
   */
  public static function log($var, $comment ='') {
    $file = '/tmp/btr.log';
    $content = "\n==> $comment: " . print_r($var, true);
    file_put_contents($file, $content, FILE_APPEND);
  }
}
0
dashohoxha

Alle Funktionsdateien in eine Datei einschließen und dann einschließen

// Datei 1
db_fct.php

// Datei 2
util_fct.php

// In a functions.php alle anderen Dateien einschließen

<?php

require_once 'db_fct.php';
require_once 'util_fct.php';
?>

Fügen Sie functions.php ein, wenn Sie Funktionen benötigen.

0
BBeta

neue Funktionen\Debug () mit Autoload lädt Funktionen in den Root-Namespace.

 Namespace Funktionen 
 {

 Klasse Debuggen 
 {
 } 
} 
 Namespace 
 {

 if (! function_exists ('printr')) {

 /**
 * 
 * @paramischer $ -Ausdruck gemischt 
 */
 Funktion printr () 
 {
 foreach (func_get_args () als $ v) {
 if (is_scalar ($ v)) {
 echo $ v. "\ n"; 
 } else {
 print_r ($ v); 
 } 
 } 
 Ausfahrt();
 } 
 } 
} 
0
Ares