戏说PHP的嵌套函数
PHP很早就支持嵌套函数了。并是不PHP5.3有闭包时才有的。然而,它却不是象JS,AS那样的闭包嵌套。即它的嵌套函数根本无闭包模式的逃脱。
PHP嵌套函数有一些特别之处。最特别的是,当外部函数被调用时,内部函数就会自动进入全局域中,成为新的定义函数。
所以,当外部函数确保是被调用一次,不会被调用二次,那么,可以写嵌套函数在其中。否则,就会引发致命错误。
但若我们仍想在一个可被调用多次的函数中定义一个内部函数,那么,该如何处理?
我们象在全局定义函数一样使用:
if (!function_exists('你的函数名')){
}
因此,全局函数的使用,常常用于一些特别的目的。同时要清楚,这样的函数,实际就是定义的全局函数。因此,它没有类对它封装,更没有命名空间。
看一下PHP手册中是如何说的:
- <?php
- function foo()
- {
- function bar()
- {
- echo "I don't exist until foo() is called.\n";
- }
- }
- /* 现在还不能调用bar()函数,因为它还不存在 */
- foo();
- /* 现在可以调用bar()函数了,因为foo()函数
- 的执行使得bar()函数变为已定义的函数 */
- bar();
- ?>
<?php
function foo()
{
function bar()
{
echo "I don't exist until foo() is called.\n";
}
} /* 现在还不能调用bar()函数,因为它还不存在 */ foo(); /* 现在可以调用bar()函数了,因为foo()函数
的执行使得bar()函数变为已定义的函数 */ bar(); ?>
PHP 中的所有函数和类都具有全局作用域,可以在内部定义外部调用,反之亦然。
我们不妨先看一下函数:
- function outer( $msg ) {
- function inner( $msg ) {
- echo 'inner: '.$msg.' ';
- }
- echo 'outer: '.$msg.' ';
- inner( $msg );
- }
- inner( 'test1' ); // Fatal error: Call to undefined function inner()
- //上面出错,是因为外部函数还没有调用,所以出错。
- outer( 'test2' ); // outer: test2 inner: test2
- inner( 'test3' ); // inner: test3
- outer( 'test4' ); // Fatal error: Cannot redeclare inner()
- //上面出错,是因为,外部函数被调用时,内部函数被重定义了。
function outer( $msg ) {
function inner( $msg ) {
echo 'inner: '.$msg.' ';
}
echo 'outer: '.$msg.' ';
inner( $msg );
} inner( 'test1' ); // Fatal error: Call to undefined function inner()
//上面出错,是因为外部函数还没有调用,所以出错。
outer( 'test2' ); // outer: test2 inner: test2
inner( 'test3' ); // inner: test3
outer( 'test4' ); // Fatal error: Cannot redeclare inner()
//上面出错,是因为,外部函数被调用时,内部函数被重定义了。
这里,我们再看一下,一个自动加载类,其中的做法
- static public function initAutoload(){
- //初始化Autoload Callable List
- self::setAutoloadCallableList();
- //初始化 $classList
- self::$classList = uxAutoloadConfig::getClassList();
- //如果有spl_autoload_register,则直接设置
- if (function_exists('spl_autoload_register')){
- ini_set('unserialize_callback_func', 'spl_autoload_call');
- spl_autoload_register(array('uxAutoload', 'splSimpleAutoload'));
- }elseif (!function_exists('__autoload')){ //否则要使用__autoload函数。
- ini_set('unserialize_callback_func', '__autoload');
- //因为没有spl_autoload, 所以, 这里要定义一个__autoload函数.
- function __autoload($class){
- if( self::splSimpleAutoload($class)== true)
- return true;
- //因为没有spl_autoload_register,所以在类未加载成功时,要处理Callable List
- foreach(self::$autoloadCallables as $key => $callable ){
- if (class_exists($class, false)){
- $classObj=self::$autoloadObjectList[$callable[0]];
- }else{
- $className=$callable[0];
- $classObj = new $className();
- self::$autoloadObjectList[$class] = &$classObj;
- }
- if (method_exists($classObj,$callable[1])){
- $method=$callable[1];
- if ($classObj->$method($class)==true)
- return true;
- }else{
- trigger_error('Autoload method '.$method.' not found in class '.$className.'!', E_USER_ERROR);
- return false;
- }
- }
- }
- }
- }
static public function initAutoload(){
//初始化Autoload Callable List
self::setAutoloadCallableList();
//初始化 $classList
self::$classList = uxAutoloadConfig::getClassList(); //如果有spl_autoload_register,则直接设置
if (function_exists('spl_autoload_register')){
ini_set('unserialize_callback_func', 'spl_autoload_call');
spl_autoload_register(array('uxAutoload', 'splSimpleAutoload'));
}elseif (!function_exists('__autoload')){ //否则要使用__autoload函数。
ini_set('unserialize_callback_func', '__autoload'); //因为没有spl_autoload, 所以, 这里要定义一个__autoload函数.
function __autoload($class){
if( self::splSimpleAutoload($class)== true)
return true;
//因为没有spl_autoload_register,所以在类未加载成功时,要处理Callable List
foreach(self::$autoloadCallables as $key => $callable ){
if (class_exists($class, false)){
$classObj=self::$autoloadObjectList[$callable[0]];
}else{
$className=$callable[0];
$classObj = new $className();
self::$autoloadObjectList[$class] = &$classObj;
}
if (method_exists($classObj,$callable[1])){
$method=$callable[1];
if ($classObj->$method($class)==true)
return true;
}else{
trigger_error('Autoload method '.$method.' not found in class '.$className.'!', E_USER_ERROR);
return false;
}
}
}
}
}
很明显,它是定义了一个内部函数function __autoload($class),以防没有'spl_autoload_register'。而这个类的这个函数,任一request请求中,只运行一次。
但是,如果要运行多次,比如,以下函数中,定义了一个全局的TRACE函数。这个函数的目的是在用户使用标准MVC方式时,才提供此TRACE函数给用户使用。引导用户走正确的方向。实际上,也可以看出,如果用户用不到此类,很可能,TRACE函数就不是这么几行代码。由此,这一做法确实精简了相当多的代码。
- static public function getInstance($config = 0 ,$className=NULL){
- if (!function_exists('trace')){ //specially for ajax debug!!
- function trace($var){
- $string=print_r($var,true);
- require_once(UXERHDIR.'../uxLogger/uxLogger.class.php');
- uxLogger::getInstance()->logg('INFO',
- '/*************************** BEGIN INFO BY TRACE: ***************************\r\n '
- .$string
- .'/*************************** END INFO BY TRACE ***************************\r\n' );
- }
- }
- if (!isset(self::$instance)){
- if (is_array($config)){
- $options=$config;
- }else{
- if ($config == NULL)
- $config = 0;
- $options=uxErrorHandlerConfig::get($config);
- }
- $class=($className==NULL)?'uxErrorHandler':$className;
- self::$instance = new $class($options);
- }
- return self::$instance;
- }
static public function getInstance($config = 0 ,$className=NULL){
if (!function_exists('trace')){ //specially for ajax debug!!
function trace($var){
$string=print_r($var,true);
require_once(UXERHDIR.'../uxLogger/uxLogger.class.php');
uxLogger::getInstance()->logg('INFO',
'/*************************** BEGIN INFO BY TRACE: ***************************\r\n '
.$string
.'/*************************** END INFO BY TRACE ***************************\r\n' );
}
}
if (!isset(self::$instance)){
if (is_array($config)){
$options=$config;
}else{
if ($config == NULL)
$config = 0;
$options=uxErrorHandlerConfig::get($config);
}
$class=($className==NULL)?'uxErrorHandler':$className;
self::$instance = new $class($options);
}
return self::$instance;
}
可以看出,嵌套函数,是一种有条件全局函数,你可以控制,在什么情况下提供这样的全局函数给用户使用。但也需要注意,过多的全局函数则会产生“全局污染”,所以,不可多用。
戏说PHP的嵌套函数的更多相关文章
- python嵌套函数、闭包与decorator
1 一段代码的执行结果不光取决与代码中的符号,更多地是取决于代码中符号的意义,而运行时的意义是由名字空间决定的.名字空间是在运行时由python虚拟机动态维护的,但是有时候我们希望能将名字空间静态化. ...
- swift 闭包+嵌套函数+extension+单例+嵌套函数+??
//: Playground - noun: a place where people can play import UIKit //*******************嵌套函数********* ...
- python之6-3嵌套函数
1. 嵌套函数 子函数可以继承父函数的变量 父函数返回子函数 子函数返回结果 看例子如下:结果是一个字符串fun1+fun2 #!/usr/bin/env python # coding=utf-8 ...
- Python函数学习——作用域与嵌套函数
全局与局部变量 在函数中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量. 全局变量作用域是整个程序,局部变量作用域是定义该变量的函数. 当全局变量与局部变量同名时,在定义局部变量的函数内 ...
- 嵌套函数变量修改nonlocal & 全局变量修改global
前几天在做一个简单的界面,单击Radiobutton保存字符串,在一个嵌套函数里面修改外部函数.一直不知道怎么修改,上网查了一下,搜关键字“嵌套函数修改变量”,找了好久,才得以解决. 对于python ...
- PHP中嵌套函数被调用时出现报错的问题
对于初入门的PHP新手来说,在学习关于PHP函数嵌套的知识点时可能会有一定的难度.比如有的朋友在练习PHP函数嵌套相关问题时,会遇到调用内部函数时就会出现报错的情况等. 那么本篇文章就为大家详细得分析 ...
- Python3学习之路~3.2 递归、函数式编程、高阶函数、匿名函数、嵌套函数
1 递归 在函数内部,可以调用其他函数.如果一个函数在内部调用自身本身,这个函数就是递归函数. def calc(n): print(n) if int(n / 2) == 0: return n r ...
- PHP:第三章——PHP中嵌套函数和条件函数
PHP中的嵌套函数: <?php header("Content-Type:text/html;charset=utf-8"); function A(){ echo &qu ...
- node.js中的匿名函数, 回调函数和嵌套函数
定义一个函数相信大家已经很熟悉了, 在javascript里的函数也是非常重要的, 使用率非常高, 有几种函数不是很好理解 一, 匿名函数 var remove = function(num1) { ...
随机推荐
- ◆ 火狐浏览器去除JS方法:
◆ 火狐浏览器去除JS方法: 在火狐地址栏输入about:config 回车 在搜索地址栏中输入javascript.enabled 右键 当一行的中的,值由false变成trun,就OK了 .
- leetcode 107
107. Binary Tree Level Order Traversal II Given a binary tree, return the bottom-up level order trav ...
- 学习BFC
BFC全称是Block Formatting Context,即块格式化上下文.它是CSS2.1规范定义的,关于CSS渲染定位的一个概念.要明白BFC到底是什么,首先来看看什么是视觉格式化模型. 视觉 ...
- JavaScript中的Get和Set访问器
今天要和大家分享的是JavaScript中的Get和Set访问器,和C#中的访问器非常相似. 标准的Get和Set访问器的实现 function Field(val){ this.va ...
- Android IOS WebRTC 音视频开发总结(三九)-- win10升级为何要p2p
本文主要介绍webrtc p2p的应用场景,文章来自博客园RTC.Blacker,支持原创,转载请说明出处. P2P最简单的解释就是两个客户端之间直接进行数据交互,不经过服务端转发. 最早接触P2P是 ...
- 将base64格式的字符串生成文件
using System; using System.IO; namespace ConsoleApplication1 { class Program { static void Main(stri ...
- Unable to add App ID because the '10' App ID limit in '7' days has been exceeded.
Unable to add App ID because the '10' App ID limit in '7' days has been exceeded. 官方的原因是对bundle iden ...
- Composer -- PHP依赖管理的用法
1:下载 1.1:方法一: 通过PHP来安装 cd /home/composer curl -sS https://getcomposer.org/installer | php #这个命令会下载c ...
- JAVA虚拟机学习笔记(一)Windows10下编译OpenJDK8
转载请注明源地址:http://www.cnblogs.com/lighten/p/5906359.html 1. 编译环境的准备 1.1 JDK源码下载 OpenJDK是JAVA发展史中的一个开源项 ...
- oracle 数组类型
create or replace function my_test(p_str varchar2) return number as --普通变量 v_var ); --固定长度数组 type v_ ...