先週、__autoload()
関数を作成することでクラスをプロジェクトに含めることができることを学びました。それから、オートローダーの使用は技術であるだけでなく、パターンでもあることを学びました。
現在、プロジェクトでオートローダーを使用していますが、非常に便利です。関数でも同じことができるのではないかと思っていました。関数を含む適切なPHPファイルを含めることを忘れると非常に便利です。
では、関数オートローダーを作成することは可能ですか?
先週、__autoload()
関数を作成することでクラスをプロジェクトに含めることができることを学びました。それから、オートローダーの使用は技術であるだけでなく、パターンでもあることを学びました。
現在、プロジェクトでオートローダーを使用していますが、非常に便利です。関数でも同じことができるのではないかと思っていました。関数を含む適切なPHPファイルを含めることを忘れると非常に便利です。
では、関数オートローダーを作成することは可能ですか?
関数の関数オートローダーはありません。4 つの現実的な解決策があります。
すべての関数を名前空間クラスにラップします (状況に応じて)。という関数があるとしstring_get_letters
ます。StringFunctions
それを静的関数として呼び出されるクラスに追加できます。したがって、 を呼び出す代わりに、 をstring_get_letters()
呼び出しますStringFunctions::get_letters()
。次に、__autoload
それらの名前空間付きクラスを作成します。
すべての機能をプリロードします。クラスを使用しているので、それほど多くの関数を持つべきではないので、プリロードするだけです。
関数を使用する前にロードします。各ファイルで、require_once
そのファイルで使用される関数ファイル。
そもそも関数を使わないでください。OOP コードを開発している場合 (とにかくそうであるように見えます)、関数はほとんど、またはまったく必要ないはずです。関数(または複数)が必要なすべてのものを、オブジェクト指向の方法で構築し、関数の必要性を回避できます。
個人的には、正確なニーズとコードベースの品質とサイズに応じて、1、2、または 4 のいずれかをお勧めします...
致命的なエラーをキャッチし、不足している関数を含めて実行しようとした醜いハックについて、しばらく前に読んだことがありますが、私は間違いなくその道には行きません。
あなたが持っている最も近いものは、__call()
魔法のメソッドです。これは、関数ではなくメソッドの一種です__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);
これにより、functionToLoad()
./path/to/functions/functionToLoad.php で定義された関数が呼び出されます。
さて、いつものように、そのための PECL 拡張があります。
(経由: http://phk.tekwire.net/joomla/support/doc/automap.htm )
関数とクラスを自動ロードすることになっています。ただし、現在の PHP インタープリターではまだ動作しません。
(代わりのオプションは、対応する名前空間をロードして実行するスタブ関数を生成することです。)
そうは言っても。オートローディングは一般的に良い方法とは考えられていません。それは過度に分断されたクラス階層とオブジェクトの幸福につながります. そして、PHP にオートローディングがある本当の理由は、インクルードと依存関係の管理システムが未熟だからです。
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 の例で使用しました。
Class と__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())
new Functions\Debug() は関数をルート名前空間にロードします。
名前空間関数 { クラスデバッグ { } } 名前空間 { if (! function_exists('printr')) { /** * * @param 混合 $expression */ 関数 printr() { foreach (func_get_args() as $v) { if (is_scalar($v)) { $v をエコーします。"\n"; } そうしないと { print_r($v); } } 出口(); } } }
すべての関数ファイルを 1 つのファイルに含めてから、それを含めます
//ファイル 1
db_fct.php
//ファイル 2
util_fct.php
// functions.phpに他のすべてのファイルを含めます
<?php
require_once 'db_fct.php';
require_once 'util_fct.php';
?>
関数が必要なときはいつでもfunctions.phpをインクルードしてください。
このディスカッションでの提案に基づいた、かなり複雑な例をもう 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);
}
}
関数と定数を自動ロードすることはできませんが、jesseschalken/autoload-generator のようなものを使用できます。これにより、どのファイルに自動ロードできないものが含まれているかが自動的に検出され、熱心にロードされます。