Go to comments

PHP 函数

一、函数的概念

函数是非常重要的概念,无论是C语言这样的面相过程式的程序,还JAVA这种面相对象式的程序都离不开函数,当然面相对象的时候,函数不叫函数叫方法。

1、概念

函数是有特定功能的代码。

可以把某些常用而特定的功能写一个函数,需要实现这个功能时,可以调用这个函数。

从而达到重用代码和简化开放的目的。

2、从编程的角度来理解函数

下面是最简单的一个函数

/**
 * 从编程的角度看
 * 把若干语句,封装在一块,起一个名字,
 * 下次根据名字来调用这个封装的块,就可以称之为函数。 
 *
 */

function QiCha(){
    echo '把水灌进水壶,烧开<br/>';
    echo '把茶叶放进茶杯<br/>';
    echo '往茶杯冲开水<br/>';
    echo '端上茶杯<br/>';
}

QiCha();

3、从数学的角度来理解函数

再通过数学的角度来理解函数,高中的时候学过函数

数学里面函数的概念:函数是变量之间的关系

比如:y = 2 * x;

当x=1,则算出y=2

当x=3,则算出y=6

……

……

我们可以看出,y与x之间,存在一种关系,

返回的y是根据x的值乘以2

也就是说y这个量和x这个量,他们之间有关系,形成了函数


写一个“真实男友数量推测机”

/**
 * 问男生的时候:你谈过几个女朋友
 * 男生真实谈过的女朋友 == 他说的个数/3
 * 女生真实谈过的男朋友 == 她说的个数*3
 *
 * 推测一个女生交过男朋友的真实数量
 * $real = $say * 3; 
 *
 * 她说的个数的三倍
 * 给我一个数,我帮你算出一个3倍的属性 —— 这样一个量与量的关系
 *
 * 先给函数一个值,然后函数乘以3,函数再还给我,仔细看这三个过程
 *
 * 此时的函数,像不像一个豆浆机
 * 入口处输入:黄豆
 * 机器内:搅拌黄豆
 * 出口:豆浆
 *
 */

function she($say){ // 参数是入口

    $real = $say * 3; // 函数体是加工

    return $real; // 出口

}

$num = 5;

echo '大s说她谈过' ,$num, '次恋爱<br/>';

echo '推测她真实谈过' ,she($num), '次恋爱';


通过两个例子学了函数的概念

1). 第一个例子:从编程的角度学函数,从编程的角度,把若干语句封装在一块,起一个名字,就可以来调用了

2). 第二个例子:从数学的角度,其实第二个例子更能说明函数的本质,有入口,有处理,有出口。然后给喊传一个参数,函数就给处理了

     注意,函数调动会有一返回值,返回的值就是return的值

二、函数的声明语法

最简单的函数声明

写一个函数名包装几个语句,可以没有参数

function foo(){

}


也可以带有参数

可以有1个或N个参数,然后处理完参数可以return一个值,也可以不return值

function foo($arg1, $arg2, $arg3){

    // return 值;

}


函数就是一个加工机器

输入黄豆,加工,return 豆浆

输入苹果,加工,return 罐头

输入淑女,加工,return 辣妹

函数声明的语法,可以是最简单的包装几个语句,复杂一点可以带几个参数,处理完参数给我们return值,也可以不return值。

三、函数的调用

函数以其名称来调用

function foo(){
    echo 'foo';
}

foo(); // foo

如果函数指定了参数,还需要传参

function bar($num){

    echo $num * 10;

}

// bar(); // 没有传参数,这样调用就出错了

bar(9); // 90

四、函数的命名规范

函数的命名规范和变量一样,以字母、数字、下划线组合,但数字不能开头

函数和变量不一样的地方,函数名字不区分字母的大小写。

以后学面相对象的时候,方法(理解成函数)也是不区分字母小写的

function foo(){

    echo 'foo';

}

FOO(); // 函数的名字是不区分大小写的

五、形参与实参的概念

长方形面积的公式:

小学时:长方形面积 = 长 * 高(三年级听不懂,听成"长城高"?)

初中时:s = a * b(s表示面积,a和b表示长和宽)

高中时:s = α * β(阿尔法 乘以 贝塔)


分析这三个公式,变的是什么,不变的是什么?

用来表示矩形两个边的“名称”变了

长   宽

a    b

α    β

不变的是关系,既无论用哪种方式,面积始终等于长乘宽,这层关系。


/**
 * 参数只是一个变量名,在函数体内发挥作用,参数的值来自于调用时,传递的值。
 *
 * 函数内部声明了2个变量,$长、$宽
 * 1. 把接收到的值,按照顺序分别赋给形参
 *    此时形参已经有值了
 * 2. 函数体执行
 * 3. 最后返回
 *
 */

function 面积($长, $宽){

	// $长,等于传过来的10
	// $宽,等于传过来的8
	// 然后10 * 8 的结果return回来

	return $长 * $宽;
}

echo 面积(10, 8); // 80



/**
 * function s($a, $b){
 *
 *     // 把外界传的的值,赋给函数声明的参数变量
 *     // $a = 10;
 *     // $b = 8;
 *
 *     return $a * $b;
 * }
 *
 * 调用 s(10, 8);
 * 无论,参数叫($a, $b),还是叫($长, $宽)
 * 都没关系,因为参数的计算关系没有变
 * 
 * 一定要注意:
 * 外界的值传给了,函数参数对应的变量,
 * 而函数的参数($a, $b)、($长, $宽)都是在函数体内发挥的作用
 *
 */

function s($a, $b){

    return $a * $b;

}

echo s(10, 8); // 80


看下面这个例子

/**
 * she函数的调用的时候
 * 传的参数$num,真正用的是什么?
 * 用的是$num的值
 *
 * 函数先在函数体内声明$say变量
 * 然后再把$num的值,赋给$asy
 * 执行
 * 返回
 *
 */

function she($say){
    
	// 模拟执行如下
    // $say = $num; 
	
    // $num就是把自己的值读出来,赋给了$say
    // 而且$say还是在函数内部发挥的作用

    $real = $say * 3;

    return $real;

}

$num = 5;

echo she($num); // 15


声明函数时的参数,叫形式上的参数 ——> 形参

调用函数时的参数,叫实际传递的参数 ——> 实参


说白了:

形参在函数体内提供的是"变量名"

实参在函数体内提供的是"值"


考考你,对形参、实参理解了没有

function foo($num){
    $num +=1;
    $num *=2;
    $num /=3;
    $num +=999;
    return $num;
}

$price = 100;

echo foo($price),'<br/>'; // 1066.3333333333

echo $price; // 100


/**
 * $price根本就没变还是100
 * 因为,在函数运算时候,$price的"值"传递给了$num
 * 因此,函数体内的$num,无论怎么变,和$price无关了
 *
 */

六、参数的传值详解

1、形参与实参的顺序

/**
 * 函数在调用的过程中
 * 实参,形参,从左 -> 到右,严格的逐一对应(对号入座),
 * 然后实参的值,挨个儿赋给形参
 * 1 赋值给 $x
 * 2 赋值给 $y
 * 3 赋值给 $z
 *
 */

function foo($x, $y, $z){
	return 2*$x + 3*$y + $z;
   // 执行:2*1  + 3*2  + 3 
}

echo foo(1, 2, 3); // 11

2、形参与实参的个数的关系

如果形参、实参没对应上

比如实参少于形参,函数有3个形参,只传2个实参给函数

function foo($x, $y, $z){

	return 2*$x + 3*$y + $z;

}

echo foo(1, 2); // 报错 Fatal error: ……

echo 'PHP7中不会往下执行,这行是看不到的';


这次是实参多余形参,会是怎么样呢?

function foo($x, $y, $z){

	return 2*$x + 3*$y + $z;
   // 执行:2*1  + 3*2  + 3 

}

echo foo(1, 2, 3, 4, 5, 6, 7); // 11

/**
 * 输出 11
 * 因为实参对号给形参赋值的时,后面多出来的实参,将会被忽略掉,没有其它副作用
 *
 */

3、参数的默认值

/**
 * 介绍自己的国籍:
 * 在国内基本都是中国人,就不需要介绍自己的国籍
 * 所以,要是说自己是某国人,就说我来自某国
 * 如果不指明自己的国籍,就默认为中国人
 *
 * ps: country 和 nationality国家与国籍
 *
 */

function intro($country = '中国'){

    return '我来自'.$country;

}

echo intro(),'<br/>'; // 我来自中国

echo intro('美国'),'<br/>'; // 我来自美国

echo intro('日本'),'<br/>'; // 我来自日本

有默认值的参数,并不能改变,实参与形参,严格按顺序赋值的原则。

$x已经有默认值了,就传两个值行吗?

function sum($x=4, $y, $z){
    return 2 * $x + 3 * $y + $z;
}

sum(5, 6); // 报错 Fatal error

/**
 * 分析:
 * 实参与形参,严格按顺序赋值的原则不会变
 * 5给了$x
 * 6给了$y
 * 因此没有值给$z
 *
 */

想一想,既然有了默认参数,调用时,又没能少传实参?

怎么才能少传一个实参,利用上默认的参数?

sum(, 5, 6);  用逗号把第一个参数空出来,这样也不行语法错误

如果参数有默认值,要把该参数,写到最后面

function sum($x, $y=3, $z=4){

    return $x + 2 * $y + 3 * $z;

}

echo sum(1),'<br/>'; // 19

echo sum(1, 2); // 17

总结

1). 函数可以设置默认值

     有默认值的参数,一般在后面

2). 如果某个有默认值的形参,他对的位置上传了实参,

     那么,实参的值将要覆盖默认值。

七、函数与变量的作用域

$num = 5;

function t(){
    $num = 9;
}

t();

echo $num,'<br/>'; // 5

/***
 * 默认状况下:
 * 1.函数运行时,有其独立的变量空间。
 * 2.函数体内的变量,与外界的变量,无关。
 * 3.即使是变量的名字相同,也无关
 * 4.所以打印输出 5
 *
 */

 

当函数调用时候,系统会申请一块独立的“内存空间”

函数体内的变量,在其独立的“调用空间”里,与外界的变量无关

函数调用结束后,申请的独立的“调用空间”,就释放掉了

下次,再调用,再申请,函数还得“从头初始化”


函数内部的叫:局部变量

函数外部的叫:全局变量


$num = 5;

function t(){
    $num = 9;
}

t();


// 再写一个函数t2
function t2(){
    echo $num;  // Notice: Undefined variable: num in
}

t2();

/**
 * 函数是独立的,函数请求执行一次,就申请一块内存空间执行一次,执行完后立即就释放掉
 * 释放掉了,下次在调用,再申请空间,再重新初始化
 *
 * 1. 因此t2()的调用,不关t函数的事
 * 2. t2的函数体内,没有$num变量
 * 3. 默认情况下,函数也不会去全局找$num
 * 3. 所以打印提示,未定义的变量 Notice: Undefined variable
 *
 */

 php和js的比较

var num = 5;

function t(){

	// var num;

	document.write(num);
}

t(); // 5


/**
 * 在js中,有作用域链的概念,在函数内找不到变量num,就会往外层寻找。
 * 在PHP中,则不会跑到外层去寻找。
 *
 * 上面只是说了现象,其实没有什么不同
 * JS中函数中访问的变量num,是没有声明就直接赋值的,所以num是属于全局的变量,在函数里面是可以访问到全局
 * 如果在函数里面声明 var num; 结果就不一样了,
 * 结果打印undefined
 *
 */

简单说,PHP函数内的变量,就局限在函数体内。

再看一道题

function t3(){
	 
	$age = 19;
	$age += 1;
	echo  $age,'<br/>';
	
}

t3(); // 20
t3(); // 20
t3(); // 20


/**
 * 函数一调用,就为为他开辟一块内存空间
 * 函数调用完,空间立即释放掉
 * 下次再调用,再申请独立的空间,从头初始化
 * ……
 *
 */

// t3(),'<br/>'; 函数后面加 <br/> 会报错

小例子:

小小的计算器,只计算加、减、乘、除

function calc($num1, $act, $num2){

    $res = null;

    switch($act){
        case '+':
            $res = $num1 + $num2;
            break;

        case '-':
            $res = $num1 - $num2;
            break;
        
        case '*':
            $res = $num1 * $num2;
            break;

        case '/':
			
            if($num2 == 0){
                echo '0不能做除数'; // 这里判断一下,如果是0,提示一下,直接break,不往下走了
                break;
            }

            $res = $num1 / $num2;
            break;
 
        default:
            $res = null;
			break;
    }

    return $res;

}


echo calc(3, '+', 2),'<br />'; // 5

echo calc(3, '*', 7),'<br />'; // 21

echo calc(3, '/', 0),'<br />'; // 0不能做除数

下面逻辑明白,但是运算符是字符串

function calc($num1, $act, $num2){

    if($act != '+' && $act != '-' && $act != '*' && $act != '/'){
        echo '不等于加减乘除,操作符有误,直接结束掉';
        exit; // 先暴力结束,其它更好的办法,明天再说
    }
	// var_dump($act);
	return $num1 . $act . $num2;
}

echo calc(3, '+', 2),'<br />'; // 3+2

echo calc(3, '*', 7),'<br />'; // 3*7

八、函数执行权与交回

1、函数的执行权

function t(){
	echo 'a<br/>';
	echo 'b<br/>';
	echo 'c<br/>';
}

echo 'x','<br/>';

t();

echo 'z','<br/>'; // z是等到,t()函数运行结束后才运行


/**
 * 页面运行结果:
 * x
 * a
 * b
 * c
 * z
 *
 */

调用函数的时候,程序的执行权,进入到函数内部

程序的执行权,进入到函数内部之后,什么时候,交出来执行权呢?


先看一个复杂一点,三个函数的嵌套

function t3(){
    echo 't3<br/>';
}

function t2(){
    echo 't2<br/>';
    t3();
}

function t1(){
    t2();
    echo 't1<br/>';
}


t1();

/**
 * 根据大原则:调用函数的时候,程序的执行权,进入到函数内部
 * 1. t1()调用执行,执行权来到t1函数体内,又执行了t2()
 * 2. t2()被调用了,执行权转的t2里面来了,
 *    输出 echo 't2<br/>'
 *    紧接着执行t3()
 * 3. 调用t3()。输出 echo 't3<br>';
 * 4. 最后都执行完了,echo 't1<br>';
 *
 * 打印的结果:
 * t2
 * t3
 * t1
 *
 */

2、执行权的交回

function foo(){
    echo 'a';
    echo 'b';
    echo 'c';
    echo '<br/>';
}

echo 'x','<br/>';

foo();

echo 'z','<br/>';

/**
 * 结果:
 * x
 * abc
 * z
 *
 */

通过这个结果,我们可以发现

当函数语句运行完之后,执行权就交回了


除了语句运行完毕,执行权交回,还有没有其它情况执行权交回?

有的,碰到rettrn之后,函数交回执行权

function bar(){
    echo 'a';

    return;

    echo 'b';
    echo 'c';
    echo '<br/>';
}

echo 'x','<br/>';

bar();

echo 'z';

/**
 * 打印结果:
 * x
 * a
 * z
 *
 */

九、函数的返回值

求“和”函数

function sum($num1, $num2){
    return $num1 + $num2;
}

$s = sum(3, 2); // 调用函数,调用函数要返回一个结果
                // 结果,就是return的值
                // sum(3, 2)返回的结果赋给变量$s 

echo $s; // 5

函数调用必有返回结果,return有返还的意思,return什么返回的就是什么

但是函数是可以没有return语句的,如果没有return语句返回什么?

function t(){
    $num = 999;
}

$s = t();

var_dump($s); // NULL

还有一种情况,有的时候函数确实结束了,但有没有必要返回值,因此只写一个return

// 没有return任何值,没有值这种值叫NULL

function t2(){
    $num = 666;
    return; // 就写一个return
    echo 'aaaa';
}

var_dump(t2()); // NULL


调用函数的返回值

1).返回return后面的值

2).如果没有return语句,或"return;"(空)

   返回值是NULL


思考:

函数可不可以return回来2个值?

高中数学知识,函数是一种映射关系,一个具体的值,只可能算出一个结果

也就是经过了一次计算,不可能算出来两个值。一个算式是具体的,算出来的结果也是具体的。

函数的定义就已经决定,只可能return一个值。

return arr(1, 2); 数组是一个值,只不过是一个符合值。

function t3(){
    return array('x',9);
}

$x = t3();

var_dump($x); // array(2) { [0]=> string(1) "x" [1]=> int(9) }

十、动态函数

函数的名字可以用变量来表示

PHP中变量可以是动态的,常量也可以是动态的,函数还可以是动态的

function wel(){
    echo '欢迎<br/>';
}

function love(){
    echo '思密达<br/>';
}

function cry(){
    echo '呜呜<br/>';
}



/**
 * 执行那个函数不一定
 * 输入哪个函数名,就执行哪个函数
 *
 * .php?func=wel
 * .php?func=cry
 * .php?func=love
 *
 */

if(isset($_GET['func'])){
    
    $func = $_GET['func'];

    // echo $func,'<br/>'; // 输入哪个函数名,就执行哪个函数

    if(function_exists($func)){
        
        $func(); // 加个小括号来调用,则会把$func的值,当做"函数名"来调用该函数
    }
        
}else{
	echo '没有传函数的名';
}

PHP的灵活之处,以后学面相对象时,"类"的名字也可以是变量

同样的操作在java里就要用反射

以后写框架的时候,就要用到这种强大特性

ps:PHP也可以用call_user_func

十一、引用赋值

$age = 19;

function t($num){
	
	// $age把值赋给$num,所以$age的值还是19
    // $num = $age 相当于

    $num += 5;

}

t($age);

echo $age; // 19


下面看函数参数的“引用赋值”

形参前多了一个&符号,传的不再是传实参的值,而是传实参的地址

$age = 19;

function foo(&$num){ // 多了一个&符号,此处的传参,传的不再是传形参的值,而传的是形参的地址
    $num += 5;
}

foo($age);

echo $age; // 24

/**
 * 这次的几个为什么是24呢?
 *
 * 是这样一个过程:
 * function foo(&$num){
 *   // $num = &age;
 *	 // 此时,函数内部的$num,也指向外部全局变量$age地址的空间上去了
 *   // $num变了,外面$age也变了
 *   $num += 5;
 * }
 *
 */

函数参数也是可以“引用赋值”

同时,我们也可以看到

函数内的局部变量,和全局上的变量

并不是谁都不会碰到谁,是有机会互相影响的,

比如:引用传参,以及超级全局变量

总结

下面是课程PPT的目录,与实际视频课程有点出入。

记录下来与实际视频课程内容,对比复习函数方面的知识点。

- 函数的概念

- 函数的封装与重用

- 函数的定义格式

- 函数的返回值

- 函数的命名规范

- 参数的传值方式

- 变量函数(应该是动态函数)

- 函数及函数中变量的作用域

- PHP自带函数(感觉这个很多,字符串函数、数组函数等等一大堆)



Leave a comment 0 Comments.

Leave a Reply

换一张