php自动加载函数(PHP 基于类的自动加载实现的函数惰性加载)PHP教程 / PHP自动加载...

wufei123 发布于 2024-06-19 阅读(5)

一直在想 PHP 有类的自动载入,为啥子没有函数的自动载入呢?PHP: 类的自动加载 - Manualhttps://wiki.php.net/rfc/function_autoloadinghttps://

stackoverflow.com/questions/4737199/autoloader-for-functions总得来说就几种方案,其中 rfc 已经被废方案1:Composer files "autoload": { "files": [ "common/Infra/functions.php" ] }

php自动加载函数(PHP 基于类的自动加载实现的函数惰性加载)PHP教程 / PHP自动加载...

用 composer 动不动就几十个助手函数,90% 以上对我们的多少来说 API 来说都是一种加载负担 $vendorDir . /symfony/polyfill-mbstring/bootstrap.php, ad155f8f1cf0d418fe49e248db8c661b => $vendorDir . /react/promise/src/functions_include.php, 320cde22f66dd4f5d3fd621d3e88b98f => $vendorDir . /symfony/polyfill-ctype/bootstrap.php, 72579e7bd17821bb1321b87411366eae => $vendorDir . /illuminate/support/helpers.php, 6b06ce8ccf69c43a60a1e48495a034c9 => $vendorDir . /react/promise-timer/src/functions.php, 25072dd6e2470089de65ae7bf11d3109 => $vendorDir . /symfony/polyfill-php72/bootstrap.php, 667aeda72477189d0494fecd327c3641 => $vendorDir . /symfony/var-dumper/Resources/functions/dump.php, 2c102faa651ef8ea5874edb585946bce => $vendorDir . /swiftmailer/swiftmailer/lib/swift_required.php, ebf8799635f67b5d7248946fe2154f4a => $vendorDir . /ringcentral/psr7/src/functions_include.php, c964ee0ededf28c96ebd9db5099ef910 => $vendorDir . /guzzlehttp/promises/src/functions_include.php, a0edc8309cc5e1d60e3047b5df6b7052 => $vendorDir . /guzzlehttp/psr7/src/functions_include.php, cea474b4340aa9fa53661e887a21a316 => $vendorDir . /react/promise-stream/src/functions_include.php, 37a3dc5111fe8f707ab4c132ef1dbc62 => $vendorDir . /guzzlehttp/guzzle/src/functions_include.php, 6124b4c8570aa390c21fafd04a26c69f => $vendorDir . /myclabs/deep-copy/src/DeepCopy/deep_copy.php, cf97c57bfe0f23854afd2f3818abb7a0 => $vendorDir . /zendframework/zend-diactoros/src/functions/create_uploaded_file.php, 9bf37a3d0dad93e29cb4e1b1bfab04e9 => $vendorDir . /zendframework/zend-diactoros/src/functions/marshal_headers_from_sapi.php, ce70dccb4bcc2efc6e94d2ee526e6972 => $vendorDir . /zendframework/zend-diactoros/src/functions/marshal_method_from_sapi.php, f86420df471f14d568bfcb71e271b523 => $vendorDir . /zendframework/zend-diactoros/src/functions/marshal_protocol_version_from_sapi.php, b87481e008a3700344428ae089e7f9e5 => $vendorDir . /zendframework/zend-diactoros/src/functions/marshal_uri_from_sapi.php, 0b0974a5566a1077e4f2e111341112c1 => $vendorDir . /zendframework/zend-diactoros/src/functions/normalize_server.php, 1ca3bc274755662169f9629d5412a1da => $vendorDir . /zendframework/zend-diactoros/src/functions/normalize_uploaded_files.php, 40360c0b9b437e69bcbb7f1349ce029e => $vendorDir . /zendframework/zend-diactoros/src/functions/parse_cookie_header.php, 4a1f389d6ce373bda9e57857d3b61c84 => $vendorDir . /barryvdh/laravel-debugbar/src/helpers.php, 6506d72cb66769ba612eb2800e4b0b6e => $vendorDir . /hunzhiwange/framework/src/Leevel/Leevel/functions.php, 05a007f8491620f2bc6b891fc6e46c02 => $vendorDir . /php-pm/php-pm/src/functions.php, 0ccdf99b8f62f02c52cba55802e0c2e7 => $vendorDir . /zircote/swagger-php/src/functions.php, 629bcf4896f1b026f50c8c0a44b87e34 => $baseDir . /common/Infra/functions.php, ); 。

曾经为这些助手函数很烦恼,因为他们都不是惰性加载,并且去掉了他们$files = include __DIR__./vendor/composer/autoload_files.php; /** * Ignore the helper functions. * Because most of them are useless. */ foreach ($files as $fileIdentifier => $_) { $GLOBALS[__composer_autoload_files][$fileIdentifier] = true; } require_once __DIR__./vendor/autoload.php;。

方案2:类方法namespace Hello\World; class Foo { public static function hello(): string { return world; } }

其实本质上还是方法,当然还是类的自动加载还有一个它的变种,类当函数namespace Hello\World; class Foo { public function __invoke(): string { return world; } }。

还有变种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); } }

方案3:利用类自动导入来实现函数代码namespace MyNamespace; class a { } function a() { } function b() { }你可以use MyNamespace\a; use function MyNamespace\a; new a(); // 或者 class_exits(a::class); a();

上面的实现是否有可以改进的地方呢比如去掉 class a 的定义,不用 new a(); 这样的怪异用法呢,答案是肯定的方案4: 基于虚拟类的自动导入实现的惰性函数加载方案函数实现的原型参考return call_user_func(\\MyNamespace\\Foo\\hello_world, 1, 2);。

实现如下return fn(\\MyNamespace\\Foo\\hello_world, 1, 2);第二种用法use function MyNamespace\Foo\hello_world; return fn(function() { return hello_world(1, 2); });

第三种用法return fn(function($a, $b) { return hell0_world($a, $b); }, 1, 2);我们定义一个类 validate($fn); try { return $fn(...$args); } catch (Error $th) { $fnName = $this->normalizeFn($fn, $th); if ($this->match($fnName)) { return $fn(...$args); } throw $th; } } /** * 匹配函数. * * @param string $fn * * @return bool */ protected function match(string $fn): bool { foreach ([Fn, Prefix, Index] as $type) { if ($this->{match.$type}($fn)) { return true; } } return false; } /** * 校验类型. * * @param \Closure|string $fn */ protected function validate($fn): void { if (!is_string($fn) && !($fn instanceof Closure)) { $e = sprintf(Fn first args must be Closure or string.); throw new Error($e); } } /** * 整理函数名字. * * @param \Closure|string $fn * @param \Error $th * * @return string */ protected function normalizeFn($fn, Error $th): string { $message = $th->getMessage(); $undefinedFn = Call to undefined function ; if (0 !== strpos($message, $undefinedFn)) { throw $th; } if (is_string($fn)) { return $fn; } return substr($message, strlen($undefinedFn), -2); } /** * 匹配一个函数一个文件. * * @param string $fn * @param string $virtualClass * * @return bool */ protected function matchFn(string $fn, string $virtualClass = ): bool { if (!$virtualClass) { $virtualClass = $fn; } class_exists($virtualClass); return function_exists($fn); } /** * 匹配前缀分隔一组函数. * * @param string $fn * * @return bool */ protected function matchPrefix(string $fn): bool { if (false === strpos($fn, _)) { return false; } $fnPrefix = substr($fn, 0, strpos($fn, _)); return $this->matchFn($fn, $fnPrefix); } /** * 匹配基于 index 索引. * * @param string $fn * * @return bool */ protected function matchIndex(string $fn): bool { if (false === strpos($fn, \\)) { return false; } $fnIndex = substr($fn, 0, strripos($fn, \\)).\\index; return $this->matchFn($fn, $fnIndex); }

定义一个助手函数use Leevel\Support\Fn; if (!function_exists(fn)) { /** * 自动导入函数. * * @param \Closure|string $call * @param array $args * @param mixed $fn * * @return mixed */ function fn($fn, ...$args) { return (new Fn())($fn, ...$args); } }

实现原理如下,我们可以通过 try catch 捕捉到一个函数不存在的错误,利用函数所在命名空间的虚拟类,通过判断虚拟类 class exits 来导入一个类,触发 composer PSR 4 规则来访问路径

第一优先级,一个文件一个函数# /data/codes/php/MyNamespace/Foo/single_func.php # 虚拟类为 MyNamespace\Foo\single_func namespace MyNamespace\Foo; function single_func() { }。

使用方法fn(\\MyNamespace\\Foo\\single_func);第二优先级分组模块化:# /data/codes/php/MyNamespace/Foo/prefix.php # 虚拟类为 MyNamespace\Foo\prefix namespace MyNamespace\Foo; function prefix_a() {} function prefix_b_c_d() {}

使用方法fn(\\MyNamespace\\Foo\\prefix_a);第三优先级,index 导入# /data/codes/php/MyNamespace/Foo/index.php # 虚拟类为 MyNamespace\Foo\index namespace MyNamespace\Foo; function hello() {} function world() {}

使用方法fn(\\MyNamespace\\Foo\\world);通过这种方式,我们可以实现函数的惰性加载,当然方法都差不多目前用这个类来做函数拆分https://github.com/hunzhiwange/。

framework/tree/master/src/Leevel/Leevel/Helperhttps://github.com/hunzhiwange/framework/blob/master/src/Leevel/Support/Fn.php

注意:分组和 index 索引还是得显示定义虚拟类防止函数不存在时的 class_exits 重复载入因为 composer 使用的是 include 会出现重复载入的问题vendor/composer/ClassLoader.php。

/** * Scope isolated include. * * Prevents access to $this/self from included files. */ function includeFile($file) { include $file; }

例如:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

河南中青旅行社综合资讯 奇遇综合资讯 盛世蓟州综合资讯 综合资讯 游戏百科综合资讯 新闻87108