5.2. 函数的使用

未匹配的标注

持之以恒,方得始终!

函数就是封装的一个能完成某一件事情的代码块。
我们可以类比接口,我们需要调用它,传递一些参数,它返回一定的结果。
当然了,调用函数,有的可能不需要给参数,或者没有返回值(默认则返回null)。

调用函数

一个最简单的调用

phpinfo(); // 不需要给参数,没有返回值,它打印出php的相关配置信息

一般我们调用系统内置的函数时,最好是查下手册,看下函数原型,参数说明,使用示例等。
我们已 fopen() 为例,看下它的函数原型

fopen(
    string $filename, // 包含路径的文件名,字符串类型
    string $mode,  // 打开文件的模式,字符串类型
    bool $use_include_path = false, // 是否使用设置好的路径,有默认值,表示其是可选的,布尔值类型
    resource $context = ? // 远程打开文件使用,有默认值表示其是可选的,资源类型
): resource // 返回一个资源类型

注意:可选参数,一般都有默认值,不给就是使用的默认值。
注意:多个可选参数的情况下,我们不能这样做

fopen($filename, $mode, $context); // $context 其实是赋值给了$use_include_path参数了。

// 要想使用第四个参数,第三个参数我们必须得给。
fopen($filename, $mode, false, $context);

来实际使用 fopen 试下

$file = "./test/.htaccess";
$fp = fopen($file, 'rb');
var_dump($fp); // resource(6) of type (stream)

调用内置函数报错

可能偶尔会遇到这种情况
函数的使用
我们看到这种错误,可能有以下几种原因

  1. 函数拼写错误。
  2. php版本不对。
  3. 属于php扩展的函数,比如GD库,我们要想使用GD扩展库中的函数,则需要先开启这个扩展,载入它。

自己写一个函数

function add($n1, $n2) { // function固定语法,add函数名,()中的是参数
    return intval($n1) + intval($n2); // 返回值
}
$res = add(3, 4);
var_dump($res); // int(7)

内置函数在所有脚本中都可以访问,但是自己声明的函数,只能在声明的脚本中使用。
我们可以创建一个 func.php 文件,用来存放我们自定义的函数,在需要的脚本中,require_once() 载入即可。

注意:php中,函数名称不能重复。
有些语言可能允许函数重名(和内置函数重名,和其它自定义的函数重名),这种叫函数的重载。

可变函数

$name();  // $name的值如果是 add,则这里等价于 add()

使用参数

function create_table($data, $border = 1, $cellpadding = 4, $cellspacing = 4) {
    echo "<table border=$border cellpadding = $cellpadding cellspacing = $cellspacing>";

    reset($data);

    $value = current($data);

    while($value) {
        echo "<tr><td> $value </td></tr>";
        $value = next($data);   
    }
    echo "</table>";
}

$arr = ['Line one', 'Line two', 'Line three'];
create_table($arr, 3, 8, 8);

第一个参数是必须的,后面三个是可选的,都有默认值。
注意:可选参数一定是在必选参数的后面。并且不能跳着给。

func_num_args(), func_get_arg(), func_get_args()

借助于上面的三个函数,我们可以给任意个参数。
func_num_args() 传入参数的个数。
func_get_arg() 根据传参位置,获取一个参数。
func_get_args() 获取所有参数,放入一个数组中。

function var_args() {
    echo "参数的个数为:" . func_num_args() . PHP_EOL;

    $args = func_get_args();
    foreach($args as $arg) {
        echo $arg . PHP_EOL;
    }
}

var_args(1, 'a');
// 参数的个数为:2
// 1
// a

作用域

先看下 require(),include()

$varname = 'xxx';
require("./file.php"); // file.php中就可以直接使用到$varname变量了。

但是对于函数来说,函数外的变量是无法直接使用的,需要传参进去,或者使用 global , $GLOBALS 的方式。

我们再来回顾一下变量的作用域:

  1. 在函数内声明的变量,其作用域是从声明开始,到函数末尾,这叫函数作用域,这个变量称为局部变量。
  2. 在函数外声明的变量,其作用域是从声明处,到文件末尾,而不是函数内部,这叫全局作用域,这个变量称为全局变量。
  3. 超级全局变量在函数内外都是可见的。
  4. 使用require(), include并不影响作用域,如果它们用于函数内部,函数作用域适用,如果是外部,全局作用域适用。
  5. 关键字 global,可以在函数中,声明一个全局变量。

我们来看一些例子:

function add() {
    $var = "contents";
}
add();
echo $var; // 会正常输出contents吗,因为作用域不同,会报变量不存在的错误。

函数的使用
上面的执行顺序是:

  1. 先在全局作用域下创建了一个 $var 变量。
  2. 然后调用了 add() 函数,里面先打印 $var,但是此时是函数作用域,和外部没关系,会报变量未定义的错误。然后又在里面创建了一个 $var变量,再次打印,则有值输出。此时函数执行完毕,里面的变量自动被销毁。
  3. 最后 echo $var, 这是全局作用域下,访问的就是调用函数之前的那个 $var。
function add() {
    global $var;
    $var = 'contents';
    var_dump($var);
}

add();
var_dump($var);
//string(8) "contents"
//string(8) "contents"

上面的例子中,函数内的 $var 被声明为了全局变量,同时它又在函数内部,所以函数内外都可见。

参数的值传递,引用传递

值传递,是创建一个原来变量的副本给函数,不影响原来的变量。

function increment($value, $amount = 1) {
    $value += $amount;
}
$value = 10;
increment($value);
echo $value; // 依旧是10,函数中的改变不会影响外部的,除非使其为全局变量,或者还可以使用引用传递。

引用传递,是获取原来变量的引用,函数里面的改变,会影响外部的。

function increment(&$value, $amount = 1) {
    $value += $amount;
}
$a = 10;
increment($a);
echo $a; // 11

return关键字

它在函数内,有两个作用:

  1. 终止函数的继续执行,然后返回到函数调用处,继续处理后续代码。
function getMaxNum($n1, $n2) {
    if (empty($n1) || empty($n2)) {
        return 'param error'; // 不满足条件,终止函数的执行,并返回一个结果到函数的调用处
    }
    // 条件满足,处理业务
    return $n1 > $n2 ? $n1 : $n2;
}

$maxnum = getMaxNum(1);
var_dump($maxnum); // 参数少了,这里会报错
  1. 返回一个值到调用处。

递归

简单说,就是函数调用自身。其实和循环一样,都是重复的做一件事情。它比较适合处理树状结构的数据。

我们来写个递归的例子,把字符串颠倒。

function reverse_r($str) {
    // 如果字符串非空,截取前面的一位,再次传给自身,比如 hello, reverse_r('ello')
    if (strlen($str) > 0) {
        reverse_r(substr($str, 1));
    }

    echo substr($str, 0, 1); // 取第一位字符
    return;
}

reverse_r("hello"); // olleh

我们来分析下执行过程:
函数的使用
因此整个输出结果就是 olleh
注意:不要忘记给递归一个终止条件,否则会一直递归下去,直到内存耗尽。
递归会在内存中创建很多自身的副本,将产生多次函数调用的开销。

我们再用循环实现一下:

function reverse_i($str) {
    for ($i = 1; $i <= strlen($str); $i++) {
        echo substr($str, -$i, 1);
    }
    return;
}

reverse_i('hello'); // olleh

所以,除非循环不好实现,或者代码写起来非常复杂,才用递归。

如有任何侵权行为,请通知我删除,谢谢大家!
个人邮箱:865460609@qq.com

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
Junwind
讨论数量: 0
发起讨论 只看当前版本


暂无话题~