-
Notifications
You must be signed in to change notification settings - Fork 106
/
Copy pathfunctions.xml
1621 lines (1447 loc) · 42.1 KB
/
functions.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<!-- EN-Revision: ae8e5c871b1a71ea77f5b97f7929d76d4ca724ab Maintainer: avenger Status: ready -->
<!-- CREDITS: Gregory, dallas, Luffy, mowangjuanzi -->
<chapter xml:id="language.functions" xmlns="http://docbook.org/ns/docbook">
<title>函数</title>
<sect1 xml:id="functions.user-defined">
<title>用户自定义函数</title>
<para>
一个函数可由以下的语法来定义:
</para>
<para>
<example>
<title>展示函数用途的伪代码</title>
<programlisting role="php">
<![CDATA[
<?php
function foo($arg_1, $arg_2, /* ..., */ $arg_n)
{
echo "Example function.\n";
return $retval;
}
?>
]]>
</programlisting>
</example>
</para>
<simpara>
任何有效的 PHP 代码都有可能出现在函数内部,甚至包括其它函数和<link linkend="language.oop5.basic.class">类</link>定义。
</simpara>
<para>
函数名和 PHP 中的其它标识符命名规则相同。有效的函数名以字母或下划线打头,后面跟字母,数字或下划线。可以用正则表达式表示为
<code>^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$</code>。
</para>
&tip.userlandnaming;
<simpara>
函数无需在调用之前被定义,<emphasis>除非</emphasis>是下面两个例子中函数是有条件被定义时。
</simpara>
<para>
当一个函数是有条件被定义时,必须在调用函数<emphasis>之前</emphasis>定义。
</para>
<para>
<example>
<title>有条件的函数</title>
<programlisting role="php">
<![CDATA[
<?php
$makefoo = true;
/* 不能在此处调用foo()函数,
因为它还不存在,但可以调用bar()函数。*/
bar();
if ($makefoo) {
function foo()
{
echo "I don't exist until program execution reaches me.\n";
}
}
/* 现在可以安全调用函数 foo()
因为 $makefoo 值为真 */
if ($makefoo) foo();
function bar()
{
echo "I exist immediately upon program start.\n";
}
?>
]]>
</programlisting>
</example>
</para>
<para>
<example>
<title>函数中的函数</title>
<programlisting role="php">
<![CDATA[
<?php
function foo()
{
function bar()
{
echo "I don't exist until foo() is called.\n";
}
}
/* 现在还不能调用 bar() 函数,因为它还不存在 */
foo();
/* 现在可以调用 bar() 函数了,因为 foo() 函数
的执行使得 bar() 函数变为已定义的函数 */
bar();
?>
]]>
</programlisting>
</example>
</para>
<para>
PHP 中的所有函数和类都具有全局作用域,可以定义在一个函数之内而在之外调用,反之亦然。
</para>
<simpara>
PHP 不支持函数重载,也不可能取消定义或者重定义已声明的函数。
</simpara>
<note>
<simpara>
从 <literal>A</literal> 到 <literal>Z</literal>
的 ASCII 函数名是大小写无关的,不过在调用函数的时候,使用其在定义时相同的形式是个好习惯。
</simpara>
</note>
<simpara>
函数支持 <link linkend="functions.variable-arg-list">可变数量的参数</link> 和
<link linkend="functions.arguments.default">默认参数</link>。参见
<function>func_num_args</function>、<function>func_get_arg</function> 和
<function>func_get_args</function> 的函数参考以获取更多信息。
</simpara>
<para>
在 PHP 中可以调用递归函数。
<example>
<title>递归函数</title>
<programlisting role="php">
<![CDATA[
<?php
function recursion($a)
{
if ($a < 20) {
echo "$a\n";
recursion($a + 1);
}
}
?>
]]>
</programlisting>
</example>
<note>
<simpara>
但是要避免递归函数/方法调用超过 100-200 层,因为可能会使堆栈崩溃从而使当前脚本终止。
无限递归可视为编程错误。
</simpara>
</note>
</para>
</sect1>
<sect1 xml:id="functions.arguments">
<title>函数的参数和参数值</title>
<simpara>
函数参数在函数签名中声明。通过参数值列表可以传递信息到函数,即以逗号作为分隔符的表达式列表。函数在实际调用之前,值参数是从左向右求值的(<emphasis>及早</emphasis>求值),并将结果赋值给函数的参数。
</simpara>
<para>
PHP 支持按值传递参数值(默认),<link
linkend="functions.arguments.by-reference">通过引用传递参数</link>以及<link
linkend="functions.arguments.default">默认参数</link>。也支持<link
linkend="functions.variable-arg-list">可变长度参数列表</link>和<link
linkend="functions.named-arguments">命名参数</link>。
</para>
<para>
<example>
<title>向函数传递数组</title>
<programlisting role="php">
<![CDATA[
<?php
function takes_array($input)
{
echo "$input[0] + $input[1] = ", $input[0]+$input[1];
}
?>
]]>
</programlisting>
</example>
</para>
<para>
自 PHP 8.0.0 起,函数参数列表可以包含一个尾部的逗号,这个逗号将被忽略。这在参数列表较长或包含较长的变量名的情况下特别有用,这样可以方便地垂直列出参数。
</para>
<example>
<title>函数参数使用尾部逗号</title>
<programlisting role="php">
<![CDATA[
<?php
function takes_many_args(
$first_arg,
$second_arg,
$a_very_long_argument_name,
$arg_with_default = 5,
$again = 'a default string', // 在 8.0.0 之前,这个尾部的逗号是不允许的。
)
{
// ...
}
?>
]]>
</programlisting>
</example>
<sect2 xml:id="functions.arguments.by-reference">
<title>通过引用传递参数值</title>
<simpara>
默认情况下,函数参数值通过值传递(因而即使在函数内部改变参数值,它并不会改变函数外部的值)。如果希望允许函数修改它的参数值,必须通过引用传递。
</simpara>
<para>
如果想要函数的参数值始终通过引用传递,可以在函数定义中该参数的前面加上符号 &:
</para>
<para>
<example>
<title>用引用传递函数参数值</title>
<programlisting role="php">
<![CDATA[
<?php
function add_some_extra(&$string)
{
$string .= 'and something extra.';
}
$str = 'This is a string, ';
add_some_extra($str);
echo $str; // 输出“This is a string, and something extra.”
?>
]]>
</programlisting>
</example>
</para>
<para>
将常量表达式作为参数值传递给需要通过引用传递的参数是错误的。
</para>
</sect2>
<sect2 xml:id="functions.arguments.default">
<title>默认参数的值</title>
<para>
函数可以使用类似分配变量的语法定义参数的默认值。仅当参数未传递值时才使用默认值;注意传递
&null; <emphasis>不会</emphasis>分配默认值。
</para>
<para>
<example>
<title>在函数中使用默认参数</title>
<programlisting role="php">
<![CDATA[
<?php
function makecoffee($type = "cappuccino")
{
return "Making a cup of $type.\n";
}
echo makecoffee();
echo makecoffee(null);
echo makecoffee("espresso");
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Making a cup of cappuccino.
Making a cup of .
Making a cup of espresso.
]]>
</screen>
</example>
</para>
<para>
默认参数值可以是标量值、<type>array</type>、特殊类型 &null;,以及从 PHP 8.1.0 开始,使用
<link linkend="language.oop5.basic.new">new ClassName()</link> 语法的对象。
</para>
<para>
<example>
<title>使用非标量类型作为默认参数</title>
<programlisting role="php">
<![CDATA[
<?php
function makecoffee($types = array("cappuccino"), $coffeeMaker = NULL)
{
$device = is_null($coffeeMaker) ? "hands" : $coffeeMaker;
return "Making a cup of ".join(", ", $types)." with $device.\n";
}
echo makecoffee();
echo makecoffee(array("cappuccino", "lavazza"), "teapot");?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Making a cup of cappuccino with hands.
Making a cup of cappuccino, lavazza with teapot.
]]>
</screen>
</example>
</para>
<para>
<example>
<title>使用对象作为默认值(自 PHP 8.1.0 起)</title>
<programlisting role="php">
<![CDATA[
<?php
class DefaultCoffeeMaker {
public function brew() {
return "Making coffee.\n";
}
}
class FancyCoffeeMaker {
public function brew() {
return "Crafting a beautiful coffee just for you.\n";
}
}
function makecoffee($coffeeMaker = new DefaultCoffeeMaker)
{
return $coffeeMaker->brew();
}
echo makecoffee();
echo makecoffee(new FancyCoffeeMaker);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Making coffee.
Crafting a beautiful coffee just for you.
]]>
</screen>
</example>
</para>
<simpara>
默认值必须是常量表达式,不能是诸如变量,类成员,或者函数调用等。
</simpara>
<para>
注意任何可选参数都应在强制参数之后指定,否则可选参数不能在调用时省略。考虑以下示例:
</para>
<para>
<example>
<title>函数默认参数的不正确用法</title>
<programlisting role="php">
<![CDATA[
<?php
function makeyogurt($container = "bowl", $flavour)
{
return "Making a $container of $flavour yogurt.\n";
}
echo makeyogurt("raspberry"); // "raspberry" 是 $container, 不是 $flavour
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Fatal error: Uncaught ArgumentCountError: Too few arguments
to function makeyogurt(), 1 passed in example.php on line 42
]]>
</screen>
</example>
</para>
<para>
现在,比较上面的例子和这个例子:
</para>
<para>
<example>
<title>函数默认参数正确的用法</title>
<programlisting role="php">
<![CDATA[
<?php
function makeyogurt($flavour, $container = "bowl")
{
return "Making a $container of $flavour yogurt.\n";
}
echo makeyogurt("raspberry"); // "raspberry" 是 $flavour
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Making a bowl of raspberry yogurt.
]]>
</screen>
</example>
</para>
<para>
自 PHP 8.0.0 起,<link linkend="functions.named-arguments">命名参数</link>可用于跳过多个可选参数。
</para>
<para>
<example>
<title>函数默认参数正确的用法</title>
<programlisting role="php">
<![CDATA[
<?php
function makeyogurt($container = "bowl", $flavour = "raspberry", $style = "Greek")
{
return "Making a $container of $flavour $style yogurt.\n";
}
echo makeyogurt(style: "natural");
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Making a bowl of raspberry natural yogurt.
]]>
</screen>
</example>
</para>
<para>
自 PHP 8.0.0 起,<emphasis>弃用</emphasis>在可选参数之后声明强制参数。这通常可以通过删除默认值来解决,因为它永远不会被使用。唯一的例外是
<code>Type $param = null</code> 类型的参数,其中默认 &null; 使得该类型可以隐式为 null。自 PHP 8.4.0
起,此用法已弃用,而应改用显式<link linkend="language.types.declarations.nullable">可为 null 类型</link>。
<example>
<title>强制参数后声明可选参数</title>
<programlisting role="php">
<![CDATA[
<?php
function foo($a = [], $b) {} // 默认不使用;自 PHP 8.0.0 起弃用
function foo($a, $b) {} // 功能相同,无弃用通知
function bar(A $a = null, $b) {} // 自 PHP 8.1.0 起,$a 是隐式必需的
// (因为其位于必需的参数之前),
// 但隐式可为 null(自 PHP 8.4.0 起已弃用),
// 因为默认参数值为 null
function bar(?A $a, $b) {} // 推荐
?>
]]>
</programlisting>
</example>
</para>
<note>
<simpara>
自 PHP 7.1.0 起,省略未指定默认值的参数会原因引发
<classname>ArgumentCountError</classname>;在此之前的版本会引发警告。
</simpara>
</note>
<note>
<simpara>
期望通过引用传参的参数可以有默认值。
</simpara>
</note>
</sect2>
<sect2 xml:id="functions.variable-arg-list">
<title>可变数量的参数值列表</title>
<simpara>
PHP 在用户自定义函数中支持可变数量的参数值列表。由
<literal>...</literal> 语法实现。
</simpara>
<para>
参数列表可能包含 <literal>...</literal> 记号,表示该函数接受可变数量的参数值。参数值将作为
&array; 传递到指定变量中:
<example>
<title>使用 <literal>...</literal> 来访问变量参数值</title>
<programlisting role="php">
<![CDATA[
<?php
function sum(...$numbers) {
$acc = 0;
foreach ($numbers as $n) {
$acc += $n;
}
return $acc;
}
echo sum(1, 2, 3, 4);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
10
]]>
</screen>
</example>
</para>
<para>
<literal>...</literal> 也可以在调用函数时将 <type>array</type> 或 <classname>Traversable</classname> 变量或文字解包到参数值列表中使用:
<example>
<title>使用 <literal>...</literal> 来传递参数</title>
<programlisting role="php">
<![CDATA[
<?php
function add($a, $b) {
return $a + $b;
}
echo add(...[1, 2])."\n";
$a = [1, 2];
echo add(...$a);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
3
3
]]>
</screen>
</example>
</para>
<para>
可以在 <literal>...</literal>
前指定正常的位置参数。在这种情况下,只有不符合位置参数的尾部参数才会被添加到
<literal>...</literal> 生成的数组中。
</para>
<para>
也可以在 <literal>...</literal> 标记前添加 <link
linkend="language.types.declarations">类型声明</link>。如果存在这种情况,那么
<literal>...</literal> 捕获的所有参数都必须匹配参数类型。
<example>
<title>输入提示的变量参数</title>
<programlisting role="php">
<![CDATA[
<?php
function total_intervals($unit, DateInterval ...$intervals) {
$time = 0;
foreach ($intervals as $interval) {
$time += $interval->$unit;
}
return $time;
}
$a = new DateInterval('P1D');
$b = new DateInterval('P2D');
echo total_intervals('d', $a, $b).' days';
// 这将会失败,因为 null 不是 DateInterval 对象。
echo total_intervals('d', null);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
3 days
Catchable fatal error: Argument 2 passed to total_intervals() must be an instance of DateInterval, null given, called in - on line 14 and defined in - on line 2
]]>
</screen>
</example>
</para>
<para>
最后,可变参数值也可以通过在 <literal>...</literal> 前面加上 <literal>&</literal>
来通过<link linkend="functions.arguments.by-reference">引用</link>传递。
</para>
</sect2>
<sect2 xml:id="functions.named-arguments">
<title>命名参数</title>
<para>
PHP 8.0.0 开始引入了命名参数作为现有位置参数的扩展。命名参数允许根据参数名而不是参数位置向函数传参。这使得参数的含义自成体系,参数与顺序无关,并允许任意跳过默认值。
</para>
<para>
命名参数通过在参数名前加上冒号来传递。允许使用保留关键字作为参数名。参数名必须是一个标识符,不允许动态指定。
</para>
<example>
<title>命名参数的语法</title>
<programlisting role="php">
<![CDATA[
<?php
myFunction(paramName: $value);
array_foobar(array: $value);
// 不支持。
function_name($variableStoringParamName: $value);
?>
]]>
</programlisting>
</example>
<example>
<title>通过位置传参与命名参数的对比</title>
<programlisting role="php">
<![CDATA[
<?php
// 使用顺序传递参数:
array_fill(0, 100, 50);
// 使用命名参数:
array_fill(start_index: 0, count: 100, value: 50);
?>
]]>
</programlisting>
</example>
<para>
指定参数的传递顺序并不重要。
</para>
<example>
<title>参数顺序不同的示例(同上例)</title>
<programlisting role="php">
<![CDATA[
<?php
array_fill(value: 50, count: 100, start_index: 0);
?>
]]>
</programlisting>
</example>
<para>
命名参数也可以与位置参数相结合使用。此种情况下,命名参数必须在位置参数之后。也可以只指定一个函数的部分可选参数,而不考虑它们的顺序。
</para>
<example>
<title>命名参数与位置参数结合使用</title>
<programlisting role="php">
<![CDATA[
<?php
htmlspecialchars($string, double_encode: false);
// 等价于
htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, 'UTF-8', false);
?>
]]>
</programlisting>
</example>
<para>
传递多个参数值给同一个命名参数会导致 <classname>Error</classname> 异常。
</para>
<example>
<title>传递多个参数值到相同命名参数将会导致抛出 Error</title>
<programlisting role="php">
<![CDATA[
<?php
function foo($param) { ... }
foo(param: 1, param: 2);
// 错误:命名参数 $param 覆盖了之前的参数
foo(1, param: 2);
// 错误:命名参数 $param 覆盖了之前的参数
?>
]]>
</programlisting>
</example>
<para>
自 PHP 8.1.0 起,可以在解包参数后面使用命名参数。命名参数<emphasis>不能</emphasis>覆盖已解包的参数。
</para>
<example>
<title>解包后使用命名参数</title>
<programlisting role="php">
<![CDATA[
<?php
function foo($a, $b, $c = 3, $d = 4) {
return $a + $b + $c + $d;
}
var_dump(foo(...[1, 2], d: 40)); // 46
var_dump(foo(...['b' => 2, 'a' => 1], d: 40)); // 46
var_dump(foo(...[1, 2], b: 20)); // Fatal error。命名参数 $b 覆盖之前的参数
?>
]]>
</programlisting>
</example>
</sect2>
</sect1>
<sect1 xml:id="functions.returning-values">
<title>返回值</title>
<para>
值通过使用可选的返回语句返回。可以返回包括数组和对象的任意类型。返回语句会立即中止函数的运行,并且将控制权交回调用该函数的代码行。更多信息见
<function>return</function>。
</para>
<note>
<para>
如果省略了
<function>return</function>,则返回值为 &null;。
</para>
</note>
<sect2>
<title>return 的使用</title>
<para>
<example>
<title><function>return</function> 的使用</title>
<programlisting role="php">
<![CDATA[
<?php
function square($num)
{
return $num * $num;
}
echo square(4); // 输出 '16'。
?>
]]>
</programlisting>
</example>
</para>
<para>
函数不能返回多个值,但可以通过返回一个数组来得到类似的效果。
</para>
<para>
<example>
<title>返回一个数组以得到多个返回值</title>
<programlisting role="php">
<![CDATA[
<?php
function small_numbers()
{
return [0, 1, 2];
}
// 使用短数组语法将数组中的值赋给一组变量
[$zero, $one, $two] = small_numbers();
// 在 7.1.0 之前,唯一相等的选择是使用 list() 结构
list($zero, $one, $two) = small_numbers();
?>
]]>
</programlisting>
</example>
</para>
<para>
从函数返回一个引用,必须在函数声明和指派返回值给一个变量时都使用引用运算符 &:
</para>
<para>
<example>
<title>从函数返回一个引用</title>
<programlisting role="php">
<![CDATA[
<?php
function &returns_reference()
{
return $someref;
}
$newref =& returns_reference();
?>
]]>
</programlisting>
</example>
</para>
<simpara>
有关引用的更多信息, 请查看 <link
linkend="language.references">引用的解释</link>。
</simpara>
</sect2>
</sect1>
<sect1 xml:id="functions.variable-functions">
<title>可变函数</title>
<para>
PHP 支持可变函数的概念。这意味着如果一个变量名后有圆括号,PHP
将寻找与变量的值同名的函数,并且尝试执行它。可变函数可以用来实现包括回调函数,函数表在内的一些用途。
</para>
<para>
可变函数不能用于例如
<function>echo</function>,<function>print</function>,<function>unset</function>,<function>isset</function>,<function>empty</function>,<function>include</function>,<function>require</function>
以及类似的语言结构。需要使用自己的包装函数来将这些结构用作可变函数。
</para>
<para>
<example>
<title>可变函数示例</title>
<programlisting role="php">
<![CDATA[
<?php
function foo() {
echo "In foo()<br />\n";
}
function bar($arg = '')
{
echo "In bar(); argument was '$arg'.<br />\n";
}
// 使用 echo 的包装函数
function echoit($string)
{
echo $string;
}
$func = 'foo';
$func(); // 调用 foo()
$func = 'bar';
$func('test'); // 调用 bar()
$func = 'echoit';
$func('test'); // 调用 echoit()
?>
]]>
</programlisting>
</example>
</para>
<para>
也可以用可变函数的语法来调用一个对象的方法。
<example>
<title>可变方法范例</title>
<programlisting role="php">
<![CDATA[
<?php
class Foo
{
function Variable()
{
$name = 'Bar';
$this->$name(); // 调用 Bar() 方法
}
function Bar()
{
echo "This is Bar";
}
}
$foo = new Foo();
$funcname = "Variable";
$foo->$funcname(); // 调用 $foo->Variable()
?>
]]>
</programlisting>
</example>
</para>
<para>
当调用静态方法时,函数调用要比静态属性优先:
<example>
<title>Variable 方法和静态属性示例</title>
<programlisting role="php">
<![CDATA[
<?php
class Foo
{
static $variable = 'static property';
static function Variable()
{
echo 'Method Variable called';
}
}
echo Foo::$variable; // 打印 'static property'。在该作用域中需要 $variable。
$variable = "Variable";
Foo::$variable(); // 在该作用域中读取 $variable 调用 $foo->Variable()。
?>
]]>
</programlisting>
</example>
</para>
<para>
<example>
<title>复杂调用</title>
<programlisting role="php">
<![CDATA[
<?php
class Foo
{
static function bar()
{
echo "bar\n";
}
function baz()
{
echo "baz\n";
}
}
$func = array("Foo", "bar");
$func(); // 打印 "bar"
$func = array(new Foo, "baz");
$func(); // 打印 "baz"
$func = "Foo::bar";
$func(); // 打印 "bar"
?>
]]>
</programlisting>
</example>
</para>
<sect2 role="seealso">
&reftitle.seealso;
<para>
<simplelist>
<member><function>is_callable</function></member>
<member><function>call_user_func</function></member>
<member><function>function_exists</function></member>
<member><link linkend="language.variables.variable">可变变量</link></member>
</simplelist>
</para>
</sect2>
</sect1>
<sect1 xml:id="functions.internal">
<title>内部(内置)函数</title>
<para>
PHP 有很多标准的函数和结构。还有一些函数需要和特定地 PHP
扩展模块一起编译,否则在使用它们的时候就会得到一个致命的“未定义函数”错误。例如,要使用
<link linkend="ref.image">image</link> 函数中的
<function>imagecreatetruecolor</function>,需要在编译 PHP 的时候加上
<productname>GD</productname> 的支持。或者,要使用
<function>mysqli_connect</function> 函数,就需要在编译 PHP 的时候加上
<link linkend="book.mysqli">MySQLi</link> 支持。有很多核心函数已包含在每个版本的
PHP 中如<link linkend="ref.strings">字符串</link>和<link
linkend="ref.var">变量</link>函数。调用
<function>phpinfo</function> 或者 <function>get_loaded_extensions</function>
可以得知 PHP 加载了那些扩展库。同时还应该注意,很多扩展库默认就是有效的。PHP
手册按照不同的扩展库组织了它们的文档。请参阅<link linkend="configuration">配置</link>,<link
linkend="install">安装</link>以及各自的扩展库章节以获取有关如何设置 PHP 的信息。
</para>
<para>
手册中<link
linkend="about.prototypes">如何阅读函数原型</link>讲解了如何阅读和理解一个函数的原型。确认一个函数将返回什么,或者函数是否直接作用于传递的参数是很重要的。例如,<function>str_replace</function>
函数将返回修改过的字符串,而 <function>usort</function>
却直接作用于传递的参数变量本身。手册中,每一个函数的页面中都有关于函数参数、行为改变、成功与否的返回值以及使用条件等信息。了解这些重要的(常常是细微的)差别是编写正确的
PHP 代码的关键。
</para>
<note>
<simpara>
如果传递给函数的参数类型与实际的类型不一致,例如将一个 <type>array</type>
传递给一个 <type>string</type> 类型的变量,那么函数的返回值是不确定的。在这种情况下,通常函数会返回
&null;。但这仅仅是一个惯例,并不一定如此。从 PHP 8.0.0 起,这种情况下应抛出 <classname>TypeError</classname> 异常。
</simpara>
</note>
<note>
<para>
在强制模式下内置函数的标量类型默认可以为 null。自 PHP 8.1.0 起,弃用传递 &null; 到声明为非 null
的内部函数参数,并在强制类型下发出弃用通知,与用户定义函数的行为保持一致,其中标量类型需要显式标记为可以为 null。
</para>
<para>
例如,<function>strlen</function> 函数接受参数 <literal>$string</literal> 为非 null 的
&string;。由于历史原因,在强制模式下 PHP 允许此参数接受 &null;,并将参数隐式转化为
<type>string</type>,从而产生 <literal>""</literal> 值。相比之下,严格模式会抛出 <classname>TypeError</classname>。
</para>
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
var_dump(strlen(null));
// "Deprecated: Passing null to parameter #1 ($string) of type string is deprecated" as of PHP 8.1.0
// int(0)
var_dump(str_contains("foobar", null));
// "Deprecated: Passing null to parameter #2 ($needle) of type string is deprecated" as of PHP 8.1.0
// bool(true)
?>
]]>
</programlisting>
</informalexample>
</note>
<sect2 role="seealso">
&reftitle.seealso;
<para>
<simplelist>
<member><function>function_exists</function></member>
<member><link linkend="funcref">函数参考</link></member>
<member><function>get_extension_funcs</function></member>
<member><function>dl</function></member>
</simplelist>
</para>
</sect2>
</sect1>
<sect1 xml:id="functions.anonymous">
<title>匿名函数</title>
<simpara>
匿名函数(Anonymous functions),也叫闭包函数(<literal>closures</literal>),允许
临时创建一个没有指定名称的函数。最经常用作回调函数 <type>callable</type>参数的值。当然,也有其它应用的情况。
</simpara>