web-dev-qa-db-ja.com

機能のオートローダー

先週、__autoload()関数を記述することで、プロジェクトにクラスを含めることができることを学びました。その後、オートローダーを使用することは、テクニックだけでなくパターンでもあることを学びました。

今、私は自分のプロジェクトでオートローダーを使用していますが、非常に便利であることがわかりました。関数で同じことを行うことができるかどうか疑問に思っていました。その中に関数を含む正しいPHPファイルを含めることを忘れることは非常に便利です。

だから、関数オートローダーを作成することは可能ですか?

51
Shoe

関数用の関数オートローダーはありません。次の4つの現実的なソリューションがあります。

  1. すべての関数を名前空間クラスにラップします(適切なコンテキスト)。したがって、_string_get_letters_という関数があるとします。これをStringFunctionsというクラスに静的関数として追加できます。したがって、string_get_letters()を呼び出す代わりに、StringFunctions::get_letters()を呼び出します。次に、それらの名前空間クラスを___autoload_します。

  2. すべての機能をプリロードします。クラスを使用しているので、thatの多くの関数を使用するべきではないので、それらを事前にロードしてください。

  3. 関数を使用する前にロードします。各ファイルで、_require_once_は、そのファイルで使用される関数ファイルです。

  4. そもそも関数を使用しないでください。 OOPコード(とにかくあなたはそうであるように思われます)を開発している場合、関数はまったく必要ないか、まったく必要ないはずです。 OOのように構築し、関数の必要性を回避できます。

個人的には、正確なニーズとコードベースの品質とサイズに応じて、1、2、4のいずれかをお勧めします...

58
ircmaxell

プロジェクトでComposer=を使用している場合、filesディレクティブを追加できますautoloadセクション。

これは実際にオートローダーでrequire_onceを生成しますが、実際のオートロードのように感じます。あなたはそれを処理する必要がないからです。
これは遅延読み込みではありません。

Assetic からの例:

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

致命的なエラーをキャッチし、欠落している関数を含めて実行しようとしたいハックについて少し前に読みましたが、私は間違いなくその道を行きません。

最も近いものは__call()magic method です。これは、関数ではなくメソッドの__autoload()のようなものです。ニーズには十分かもしれません。クラスを呼び出して、それぞれ異なる機能を個別に必要とする余裕がある場合。 PHP 5.3.0)なので、__callStatic()もあります。

__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);
_

これは、。/ path/to/functions/functionToLoad.phpで定義されているfunctionToLoad()関数を呼び出します。

15
Alix Axel

さて、いつものように、そのためのPECL拡張機能があります。

(経由: http://phk.tekwire.net/joomla/support/doc/automap.htm

クラスと同様に関数を自動ロードすることになっています。ただし、これは現在のPHPインタープリターではまだ動作しません。

(代替オプションbtwは、名前空間に対応するものをロードおよび実行するスタブ関数を生成しています。)

それは言われています。オートロードは一般的に良い習慣とはみなされていません。それは、過度に破壊されたクラス階層とオブジェクトの幸福につながります。そして、本当の理由PHPにオートロードがあるのは、インクルード管理システムと依存関係管理システムが未熟だからです。

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);
    }

}
_

また、名前空間を使用して、Fn::myFunc()およびspl_autoload_register()を実行できます。このコードを次の例で使用しました: https://goo.gl/8dMIMj

2
Bryan Horna

クラスと __ invoke を使用します。 __invokeメソッドは、スクリプトがクラスを関数として呼び出すときに呼び出されます。私はしばしばこのようなことをします:

<?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',
        ],
      ]
    ];
  }
}

その後、関数のように呼び出すことができます:

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

新しいFunctions\Debug()は、関数をルート名前空間に読み込みます。

 namespace関数
 {
 
 class Debug 
 {
} 
} 
 namespace 
 {
 
 if(!function_exists( 'printr')){
 
 /**
 * 
 * @param mixed $ expression 
 */
 function printr()
 {
 foreach(func_get_args()as $ v){
 if( is_scalar($ v)){
 echo $ v。 "\ n"; 
} else {
 print_r($ v); 
} 
} 
 exit(); 
 } 
} 
} 
1
Ares

関数と定数を自動ロードすることはできませんが、 jesseschalken/autoload-generator のようなものを使用できます。これは、自動ロードできないものを含むファイルを自動的に検出し、積極的にロードします。

0
Jesse

この議論の提案に基づいた、もう1つのやや複雑な例を示します。コードはここにもあります: 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

すべての関数ファイルを1つのファイルに含めてから含めます

//ファイル1
db_fct.php

//ファイル2
til_fct.php

//functions.phpに他のすべてのファイルを含める

<?php

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

関数が必要なときはいつでもfunctions.phpを含めてください。

0
BBeta