博客列表 >PHP函数探讨学习

PHP函数探讨学习

吾逍遥
吾逍遥原创
2020年12月02日 18:15:04896浏览

一、PHP实现的计算器

为什么要写PHP实现的计算器,就是纠正我错误的认识,在上一篇博文中,老师布置的PHP实现计算器的作业,我还以为必须要用ajax和后端交互呢,就感觉没怎么写的必要了,结果老师再讲课时让我很汗颜,我竟然报表单忘记了,在前端授课中,老师专门强调了,前端和后端交互的 三种方式:url、表单和ajax 。结果我只记得ajax了,不可否认,ajax是最普遍使用的,但对于只需要提交数据而言,表单完全就胜任了。

另外一个,从老师单PHP文件实现计算器的案例中学习到了PHP和前端简单交互功能,没有使用你任何JS代码,给我也是一种启发,所以这里记录下来,告警自己,编程需要灵活,太死板反而不利于编写出拍案叫绝的程序来。

PHP计算器功能:

  • 能对未输入、输入超范围和输入不是整数都会错误提示
  • 能对除法时,除数为0的情况进行错误提示
  • 能过滤前端非法操作
  • 能进行加、减、乘、除和取余运算

实现解析:

  • 过滤由filter_input_array()完成,不要相信用户任何输入数据,这是处理的原则。这个过滤功能很不错同,值得拥有。
  • 计算功能由switch…case分支控制实现。
  • 另外一个为了规范,开始使用PHP模板语法,将以前{}书写习惯向模板语法过渡。
  1. <style>
  2. :root {
  3. font-size: 1.1em;
  4. }
  5. form {
  6. margin: 1em auto;
  7. }
  8. input {
  9. width: 12em;
  10. height: 1.2em;
  11. }
  12. select {
  13. width: 5em;
  14. }
  15. </style>
  16. <?php
  17. error_reporting(E_ALL & ~E_NOTICE);
  18. $messageArr = [];
  19. $result = "";
  20. if (!empty($_POST)) :
  21. // 第一步:采用过滤函数过滤输入
  22. $argFilter = array(
  23. "num1" => array("filter" => FILTER_VALIDATE_INT),
  24. "num2" => array("filter" => FILTER_VALIDATE_INT),
  25. "opt" => array("filter" => FILTER_VALIDATE_INT, "options" => array("min_range" => 0, "max_range" => 4)),
  26. "calc" => FILTER_SANITIZE_ENCODED,
  27. );
  28. $inputs = filter_input_array(INPUT_POST, $argFilter);
  29. // var_dump($inputs);
  30. // 对检验结果反馈给用户
  31. if ($inputs['num1'] === false) array_push($messageArr, "第一个数不在许可范围内或不能为空");
  32. if ($inputs['num2'] === false) array_push($messageArr, "第二个数不在许可范围内或不能为空");
  33. if ($inputs['opt'] === 3 && $inputs['num2'] === 0) array_push($messageArr, "除法时,除数不能为0");
  34. if (!empty($messageArr)) :
  35. foreach ($messageArr as $message) :
  36. echo "<p><span style='color:red'>错误警告:</span>{$message}</p>";
  37. endforeach;
  38. else :
  39. // 一切检验通过后,正常运算
  40. switch ($inputs['opt']):
  41. case 1:
  42. $result = "{$inputs['num1']} - {$inputs['num2']} = " . ($inputs['num1'] - $inputs['num2']);
  43. break;
  44. case 2:
  45. $result = "{$inputs['num1']} * {$inputs['num2']} = " . ($inputs['num1'] * $inputs['num2']);
  46. break;
  47. case 3:
  48. $result = "{$inputs['num1']} / {$inputs['num2']} = " . ($inputs['num1'] / $inputs['num2']);
  49. break;
  50. case 4:
  51. $result = "{$inputs['num1']} % {$inputs['num2']} = " . ($inputs['num1'] % $inputs['num2']);
  52. break;
  53. default:
  54. $result = "{$inputs['num1']} + {$inputs['num2']} = " . ($inputs['num1'] + $inputs['num2']);
  55. endswitch;
  56. endif;
  57. endif;
  58. ?>
  59. <form action="" method="post">
  60. <label for="num1">第一个数:</label>
  61. <input type="num" size="4" name="num1" value="<?php echo $_POST['num1']; ?>" id="num1" placeholder="输入0到10000之间的数">
  62. <select name="opt" id="opt">
  63. <option value="0" <?php echo $_POST['opt'] == '0' ? 'selected' : '' ?>>加</option>
  64. <option value="1" <?php echo $_POST['opt'] == '1' ? 'selected' : '' ?>>减</option>
  65. <option value="2" <?php echo $_POST['opt'] == '2' ? 'selected' : '' ?>>乘</option>
  66. <option value="3" <?php echo $_POST['opt'] == '3' ? 'selected' : '' ?>>除</option>
  67. <option value="4" <?php echo $_POST['opt'] == '4' ? 'selected' : '' ?>>取余数</option>
  68. </select>
  69. <label for="num2">第二个数:</label>
  70. <input type="num" size="4" name="num2" value="<?php echo $_POST['num2']; ?>" id="num2" placeholder="输入0到10000之间的数">
  71. <button name="calc" value="calc">计算</button>
  72. </form>
  73. <p>计算结果:<span style="color:green;"><?php echo empty($result) ? '' : $result; ?></span></p>

calc

二、大段字符串的简洁语法

前篇已经介绍过了 PHP模板语法简化PHP书写,减少代码量,提高可阅读性双引号简化字符串和变量的拼接 ,那么在在PHP和Html混合编写时,有时需要书写大量的html元素,可能是多行,此时再用双引号就不是那么好阅读了,是否有类似于PHP模板语法的方式来简化大量PHP和Html混合编码书写呢?答案是有的,这个在培训班发的PHP资料中第一课中,我在它基础上进行了测试,归纳出使用方法。

使用规范:

  1. 使用 <<< 做为引导符, 使用一对字符串做为标识符,结束标识符。字符串内容随意,建议全部大写。
  2. 开始标识符加双引号则类似双引号功能 ,但内部引用双号号不需要转义,可以解析变量和特殊字符。我看到有某个前期学员称它为双引号Plus版,比较开解。
  3. 开始标识符加单引号则类似单引号功能 ,适合不需要转义特殊字符和变量解板的大段文本,如html代码。可以看成单引号的Plus版
  4. 单双引号只能在开始标识符加,如果不加则默认为双引号,结束标识符禁止加引号 。不过建议加上,更符合规范。
  5. 它结果是字符串,可赋值给变量 ,再结合双引号可完成复杂的字符串拼接。
  1. $str = 'Hello World';
  2. // 开始标识符加双引号类似双引号,可解析变量和特殊字符
  3. echo <<< "HERODOC"
  4. <h3 style="color:red">"PHP\t中文网":{$str}</p>
  5. <h3 style="color:red">"PHP\t中文网":{$str}</p>
  6. HERODOC;
  7. // 开始标识符加单引号则类似单引号功能,就是普通字符串
  8. echo <<< 'NOWDOC'
  9. <h3 style="color:red">"PHP\t中文网":{$str}</p>
  10. <h3 style="color:red">"PHP\t中文网":{$str}</p>
  11. NOWDOC;
  12. // 开始标识符不加引号,默认是双引号,不过建议加上
  13. echo <<< HELP
  14. <h3 style="color:green">"PHP\t中文网":{$str}</p>
  15. <h3 style="color:green">"PHP\t中文网":{$str}</p>
  16. HELP;
  17. // 结果其实是字符串,可赋值给变量。
  18. $content = <<< "CONTENT"
  19. <h3>欢迎来到PHP世界<h3>
  20. <p>{$str}</p>
  21. <p><span>PHP是一门后端语言,占有率一直遥遥领先</span></p>
  22. CONTENT;
  23. echo $content;

php-simple

三、函数

函数是代码块复用的手段,变量是数据复用的手段。函数对特定功能进行封装,便于再次使用。

1、系统内置函数

PHP内置大量的内置函数,如前面文章中使用的date()、time()、mt_rand()、filter_list()等等

  1. // 临时设置报错级别
  2. error_reporting(E_ALL & ~E_NOTICE);
  3. // 随机数
  4. echo mt_rand() % 101, '<br>';
  5. echo mt_rand(10, 99), '<br>';
  6. // 转小写,大写是strtoupper()
  7. echo strtolower('COMPONENTS'), '<br>';
  8. // date获取日期函数
  9. echo date('Y:m:d h:m:s', time()), '<br>';
  10. // 打印函数、生成数组函数
  11. print_r(range(1, 10, 2));
  12. echo '<br>';

2、函数返回值

    1. 函数没有返回值时,返回null
    1. 函数只能返回单一的值,返回值的数据类型可以是任意类型()
    1. 函数碰到return语句,立即结束程序执行,return后面的代码不会被执行
  1. function demo1()
  2. {
  3. return fopen('log.txt', 'w');
  4. return new stdClass;
  5. return range(1, 10, 2);
  6. return true;
  7. return 'woxiaoyao';
  8. return 12;
  9. }
  10. var_dump(demo1());
    1. 间接返回多个值的解决方案: 数组、json和序列化serialize
      • 其实返回多个值一般都是通过数组实现的,无论是json还是serialize都是基于数组。
      • json是json_encode()将数组转换成json字符串返回,然后再json_decode($str,true)再还原为数组。注意第二个参数是true是表示返回数组。
      • 序列化serialize其实也是通过serialize()将数组转换成序列化字符串返回,然后于丹通过反序列化unserialize()还原成数组。
  1. function demo2()
  2. {
  3. $a = 12;
  4. $str = 'woxiaoyao';
  5. // 数组返回多个参数
  6. // return array($a,$str);
  7. // json返回多个参数,转换json是json_encode(),还原是json_decode(),若是还原数组,第二个参数为true
  8. // return json_encode(array($a,$str));
  9. // 序列化返回多个参数,序列化是serialize(),反序列化unserialize()。其中a表示数组,s表示字符串,i为整数,a和s后面是长度+内容。
  10. return serialize(array($a, $str));
  11. }
  12. var_dump(demo2());

3、形参与实参

  • 形参就是函数定义时圆括号中参数,此时函数只是代表某功能的代码块。
    • 一般情况下它是临时变量,就是在调用时再分配空间,结束时就释放。若是引用传参,则调用时指向实参空间,直接操作实参空间的数据 。
    • 可以设置默认值,这样用户不输入参数时则使用默认参数,若用户传参,则使用用户参数覆盖默认参数。
    • 参数列表是从左向右求值的
    • 若传递参数未知,则可使用剩余参数来收集,将所有参数归纳为一个数组,使用三个点为标记,这个在JS和C++都存在这个概念
  • 实参就是用户调用函数时传递的参数,此时函数就完成某项功能。
    • 实参传递有两种:一种是传值传参,只是将值给函数的形参。另一种引用传参,就是将形参指向实参的空间,直接操作实参空间的数据。
    • 对应形参的剩余参数,实参有展开参数,就是通过三个点为标识,将数组作为参数,一次性传递函数形参。

三个点标识的参数,在 形参时为剩余运算符 ,接受未知个数的参数,并归纳为数组,由函数内部使用。而在 实参时则是展开运算符 ,将数组作为参数一次性传给函数。JS中也有同样概念,展开赋值给剩余可看解构赋值。

  1. function calc(string $opt, ...$args)
  2. {
  3. $opts = ['+', '-', '*', '/', '%'];
  4. // 判断运算符是否合法
  5. if (!in_array($opt, $opts)) {
  6. return '操作运算符只能是+、-、*、/和%';
  7. }
  8. // 将数组第一个值给结果
  9. $res = array_shift($args);
  10. // 按运算符循环处理
  11. foreach ($args as $arg) :
  12. $res = eval("return {$res} {$opt} {$arg};");
  13. endforeach;
  14. return $res;
  15. }
  16. $vals = range(1, 5);
  17. // 实参展开运算符一次性赋值
  18. echo calc('*', ...$vals), '<br>';

4、回调函数(匿名函数或闭包)

回调函数是将函数作为参数传递给父函数,又称匿名函数或闭包。朱老师的解释是回调函数我们只负责定义,使用是别人的事,而灭绝老师则是用于异步编程中函数的参数,异步编程->对函数执行的时间和顺序不可预测,通过事件/回调的方式来唤醒主程序 。到这里是不是还是很难理解呢?当我看到老师在array_map()的回调函数中定义自己的行为时,又想起以前事件中回调函数,突然想通了,想理解回调函数还是从编程中发现吧!目前我遇到的回调函数最常见的 应用场景有两个:一是在回调函数中定义自己的处理方式或规则,另一个就是等待处理结束后触发的事件使用回调函数 。其实二者又可归纳为一条就是: 未知的用户需求和未知的结束时间 。用户的需求是多变的,我们提供的函数不可能满足每个用户的需求,而且不现实。如array_map提供遍历每个成员,用户定义如何处理,非常合理的思路;远程获取数据和事件的处理结果等都无法预知结束时间,此时可设定各种事件,如成功、失败、超时等,触发各自的处理,一般代码是顺序执行的,而回调函数则依靠事件驱动,可以解决此类异步的问题。

应用场景一: 对于调用者未知的需求,父函数只提供基本的功能或默认功能,回调函数允许用户自定义自己的处理方式 。 上面已经用array_map举例子了,再看我上一篇文章中过滤器filter,也提供了回调函数,允许用户自定义自己的过滤规则,你再回头看看你所遇到的回调函数,是不是有很多这样的例子,JS中有map、filter等。其实这种理念在前端也随处可见,vuejs组件中插槽slot也是允许用户定义自己的布局的,也可使用默认布局。优秀的函数库和UI组件都会给使用者一定自定义空间,否则就太死板了。

  1. $arr = range(1, 10);
  2. // 函数通过回调允许用户定义数据处理方式,它本身只提供遍历功能
  3. $newArr=array_map(function ($item) {
  4. return $item * 2 + 1;
  5. }, $arr);
  6. print_r($newArr);
  7. echo '<br>';

应用场景二: 对于未知结束时间的任务,如异步任务中通过事件来告诉父函数的任务进度,进而触发相应的回调函数处理 代码正常执行的模式是同步,即执行完一个代码块后再执行下一个代码块,而有些任务如远程获取数据、和用户进行交互如弹窗和进行大量数据运算等,结束时间是无法预知的,此时若一直等待明显是不合适的,对于这些最好的处理方式就是事件, 事件函数就是回调函数,它从代码处开始运行,事件通知后结束运行,不影响程序的主流程。这个在JS中经常见到,而swoole也有这样的例子。

关于回调与同步、异步的关系: 我们正常认为回调就是异步,其实上面两种应用场景中已经说明了,第一种是同步中的回调函数,允许用户自定义。第二种是常见的是异步中的回调函数。回调和同步、异步没有直接联系,回调只是一种实现方式,既可以有同步回调,也可以有异步回调,还可以有事件处理回调和延迟函数回调,这些在我们工作中有很多的使用场景,它们最终都是用户来自定义处理方式,回调函数只是提供了环境。

5、匿名函数的use使用

我们知道PHP中匿名函数也是函数,也无法访问外部变量,函数解决访问外部变量在https://www.php.cn/blog/detail/24902.html中提到过global关键字或$GLOBAL全局变量数组,现在有了新的解决方案就是use,注意此时 函数必须是匿名函数 才可以,如function sum() use ($a,$b){}将报错的,只有function () use ($a,$b){}才正确,而且此时 变量就是函数名 如下案例调用$res()返回结果。

  1. $a = 12;
  2. $b = 34;
  3. $res=function () use($a,$b){
  4. return $a+$b;
  5. };
  6. var_dump($res());

如果同时想改变外部变量的值,必须引用传值才可以,默认是外部变量的副本,若是function () use (&$a,&$b){}就是引用外部变量了,即可访问又可修改。

use其实就是解决匿名函数(闭包)不能访问外部变量而提的方案,这个JS完全没问题,PHP为了解决访问外部变量也是想了很多办法啊

6、函数重载

首先声明下,目前PHP还没有函数重载的概念,它是来自于C++和Java。想到它是因为老师在说明函数形参和实参时,形参是从左至右处理的,如果直接传递中间的参数是无法成功,这个我就想到了自己的老本行C++,它的重载函数真是很好用。那么PHP可以实现函数重载吗?当然可以,目前有两种方案, 一种是作用func_get_args()和func_num_args(),另一种是call_user_func()或call_user_func_array(),这里只演示第一种实现的函数重载,后来会介绍下语法,具体实现可百度下

  • func_get_args()会获取传递给函数的所有参数,它的类型是数组 ,剩余参数非常类似它,不过它功能更强大
  • func_num_args()会获取传递给函数的参数个数,它的类型是整型
  • 需要注意:它们实现的重载函数,无论是 被重载函数还是重载函数都要用return返回 ,如下面rewrite中return f1(),若是没有前面return则只返回到调用处,然后就不知道了,反正没结果。这个有待以后验证吧。

    1. function f1($a)
    2. {
    3. return "重载函数有一个参数:a={$a}";
    4. }
    5. function f2($a, $b)
    6. {
    7. return "重载函数有两个参数:a={$a}和b={$b}";
    8. }
    9. function rewrite()
    10. {
    11. $args = func_get_args();
    12. $num = func_num_args();
    13. switch ($num):
    14. case 1:
    15. return f1($args[0]);
    16. break;
    17. case 2:
    18. return f2($args[0], $args[1]);
    19. break;
    20. default:
    21. return '未定义函数';
    22. endswitch;
    23. }
    24. echo rewrite(1),'<br>';
    25. echo rewrite(1, 2),'<br>';
    • call_user_func()和call_user_func_array() 都可以调用其它函数,第一个参数是函数名,第二个参数开始时参数
      • 二者不同是,前者是参数一个一个传,而后者是作为数组一次性传,借上面讲的展开运算符
      • 另外调用类的中方法时,第一个参数又变成数组,数组中第一个数是类名,第二个方法名。
  1. function nowamagic($a,$b)
  2. {
  3. echo $a;
  4. echo $b;
  5. }
  6. call_user_func('nowamagic', "111","222");
  7. class a {
  8. function b($c)
  9. {
  10. echo $c;
  11. }
  12. }
  13. call_user_func(array("a", "b"),"111");
  14. function a($b, $c)
  15. {
  16. echo $b;
  17. echo $c;
  18. }
  19. call_user_func_array('a', array("111", "222"));

其它

  • 命名空间namespace:我们知道命名空间是为解决全局成员冲突而设计的,那么哪些是全局成员呢? 常量、函数、类(接口)都是全局成员 ,要用命名空间来解决命名冲突问题。使用格式是根空间开始,就是\,也是全局空间,然后是依次的空间名路径了,最后是方法名。如\ns1\demo()。

  • 数组一些常用函数

    • array_map 遍历数组,通过回调函数接受用户自定义行为,返回新的数组,不影响原数组。但是不符合行为的以空值返回,它常用于处理数组,不要用于筛选数组成员。
    • array_map 过滤数组,返回条件为true的成员组成的数组,不影响原数组。新的数组成员是原数组中成员中某一个,它是筛选数组。
    • array_values() 以数组的值创建新的索引数组,索引从0重新开始。
    • array_keys() 返回数组的键名。
声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议
灭绝师太2020-12-03 13:54:071楼
input type 属性的值设置为number, 可以在前端控制用户的输入只有数字有效,,另外还有一些属性值很有趣,比如date 可以在输入框唤起日期插件,可以试一下,call_user_func_array()这个函数后面面向对象中还会遇到哦,相信你不会觉得陌生~