@@ -69,7 +69,7 @@ discard block |
||
69 | 69 | * |
70 | 70 | * @param array $arguments Options that influence the construction of the XML document. |
71 | 71 | * |
72 | - * @return FluidXml A new FluidXml instance. |
|
72 | + * @return FluidContext A new FluidXml instance. |
|
73 | 73 | */ |
74 | 74 | function fluidify(...$arguments) |
75 | 75 | { |
@@ -86,6 +86,9 @@ discard block |
||
86 | 86 | return new FluidNamespace(...$arguments); |
87 | 87 | } |
88 | 88 | |
89 | +/** |
|
90 | + * @param string $string |
|
91 | + */ |
|
89 | 92 | function is_an_xml_string($string) |
90 | 93 | { |
91 | 94 | // Removes any empty new line at the beginning, |
@@ -536,7 +539,7 @@ discard block |
||
536 | 539 | * $xml->query("/doc")->query("book[@id='123']"); |
537 | 540 | * ``` |
538 | 541 | * |
539 | - * @param string $xpath The XPath to execute. |
|
542 | + * @param string[] $xpath The XPath to execute. |
|
540 | 543 | * |
541 | 544 | * @return FluidContext The context associated to the DOMNodeList. |
542 | 545 | */ |
@@ -558,9 +561,6 @@ discard block |
||
558 | 561 | * ``` |
559 | 562 | * |
560 | 563 | * @param string|array $child The child/children to add. |
561 | - * @param string $value The child text content. |
|
562 | - * @param bool $switchContext Whether to return the current context |
|
563 | - * or the context of the created node. |
|
564 | 564 | * |
565 | 565 | * @return FluidContext The context associated to the DOMNodeList. |
566 | 566 | */ |
@@ -575,6 +575,10 @@ discard block |
||
575 | 575 | public function remove(...$xpath); |
576 | 576 | public function xml($strip = false); |
577 | 577 | // Aliases: |
578 | + |
|
579 | + /** |
|
580 | + * @return FluidContext |
|
581 | + */ |
|
578 | 582 | public function add($child, ...$optionals); |
579 | 583 | public function prepend($sibling, ...$optionals); |
580 | 584 | public function insertSiblingBefore($sibling, ...$optionals); |
@@ -636,6 +640,9 @@ discard block |
||
636 | 640 | private $context; |
637 | 641 | private $times; |
638 | 642 | |
643 | + /** |
|
644 | + * @param FluidContext $context |
|
645 | + */ |
|
639 | 646 | public function __construct($document, $context, $times) |
640 | 647 | { |
641 | 648 | $this->document = $document; |
@@ -1072,6 +1079,9 @@ discard block |
||
1072 | 1079 | return [ $element, $attributes, $switch_context ]; |
1073 | 1080 | } |
1074 | 1081 | |
1082 | + /** |
|
1083 | + * @param \Closure $fn |
|
1084 | + */ |
|
1075 | 1085 | protected function insertElement($element, &$optionals, $fn) |
1076 | 1086 | { |
1077 | 1087 | list($element, $attributes, $switch_context) = $this->handleOptionals($element, $optionals); |
@@ -1277,6 +1287,10 @@ discard block |
||
1277 | 1287 | return $context; |
1278 | 1288 | } |
1279 | 1289 | |
1290 | + /** |
|
1291 | + * @param string $k |
|
1292 | + * @param string $v |
|
1293 | + */ |
|
1280 | 1294 | protected function insertSpecialContent($parent, $k, $v) |
1281 | 1295 | { |
1282 | 1296 | // The user has passed an element text content: |
@@ -1294,6 +1308,10 @@ discard block |
||
1294 | 1308 | return []; |
1295 | 1309 | } |
1296 | 1310 | |
1311 | + /** |
|
1312 | + * @param string $k |
|
1313 | + * @param string $v |
|
1314 | + */ |
|
1297 | 1315 | protected function insertSpecialAttribute($parent, $k, $v) |
1298 | 1316 | { |
1299 | 1317 | // The user has passed an attribute name and an attribute value: |
@@ -1305,6 +1323,10 @@ discard block |
||
1305 | 1323 | return []; |
1306 | 1324 | } |
1307 | 1325 | |
1326 | + /** |
|
1327 | + * @param string $k |
|
1328 | + * @param string $v |
|
1329 | + */ |
|
1308 | 1330 | protected function insertStringString($parent, $k, $v, $fn) |
1309 | 1331 | { |
1310 | 1332 | // The user has passed an element name and an element value: |
@@ -1316,6 +1338,9 @@ discard block |
||
1316 | 1338 | return [ $el ]; |
1317 | 1339 | } |
1318 | 1340 | |
1341 | + /** |
|
1342 | + * @param string $k |
|
1343 | + */ |
|
1319 | 1344 | protected function insertStringMixed($parent, $k, $v, $fn, &$optionals) |
1320 | 1345 | { |
1321 | 1346 | // The user has passed one of these two cases: |
@@ -1332,6 +1357,9 @@ discard block |
||
1332 | 1357 | return [ $el ]; |
1333 | 1358 | } |
1334 | 1359 | |
1360 | + /** |
|
1361 | + * @param integer $k |
|
1362 | + */ |
|
1335 | 1363 | protected function insertIntegerArray($parent, $k, $v, $fn, &$optionals) |
1336 | 1364 | { |
1337 | 1365 | // The user has passed a wrapper array: |
@@ -1348,6 +1376,10 @@ discard block |
||
1348 | 1376 | return $context; |
1349 | 1377 | } |
1350 | 1378 | |
1379 | + /** |
|
1380 | + * @param integer $k |
|
1381 | + * @param string $v |
|
1382 | + */ |
|
1351 | 1383 | protected function insertIntegerString($parent, $k, $v, $fn) |
1352 | 1384 | { |
1353 | 1385 | // The user has passed a node name without a node value: |
@@ -1359,6 +1391,10 @@ discard block |
||
1359 | 1391 | return [ $el ]; |
1360 | 1392 | } |
1361 | 1393 | |
1394 | + /** |
|
1395 | + * @param integer $k |
|
1396 | + * @param string $v |
|
1397 | + */ |
|
1362 | 1398 | protected function insertIntegerXml($parent, $k, $v, $fn) |
1363 | 1399 | { |
1364 | 1400 | // The user has passed an XML document instance: |
@@ -1388,6 +1424,10 @@ discard block |
||
1388 | 1424 | return $this->attachNodes($parent, $nodes, $fn); |
1389 | 1425 | } |
1390 | 1426 | |
1427 | + /** |
|
1428 | + * @param integer $k |
|
1429 | + * @param \DOMDocument $v |
|
1430 | + */ |
|
1391 | 1431 | protected function insertIntegerDomdocument($parent, $k, $v, $fn) |
1392 | 1432 | { |
1393 | 1433 | // A DOMDocument can have multiple root nodes. |
@@ -1399,26 +1439,46 @@ discard block |
||
1399 | 1439 | // return $this->attachNodes($parent, $v->documentElement, $fn); |
1400 | 1440 | } |
1401 | 1441 | |
1442 | + /** |
|
1443 | + * @param integer $k |
|
1444 | + * @param \DOMNodeList $v |
|
1445 | + */ |
|
1402 | 1446 | protected function insertIntegerDomnodelist($parent, $k, $v, $fn) |
1403 | 1447 | { |
1404 | 1448 | return $this->attachNodes($parent, $v, $fn); |
1405 | 1449 | } |
1406 | 1450 | |
1451 | + /** |
|
1452 | + * @param integer $k |
|
1453 | + * @param \DOMNode $v |
|
1454 | + */ |
|
1407 | 1455 | protected function insertIntegerDomnode($parent, $k, $v, $fn) |
1408 | 1456 | { |
1409 | 1457 | return $this->attachNodes($parent, $v, $fn); |
1410 | 1458 | } |
1411 | 1459 | |
1460 | + /** |
|
1461 | + * @param integer $k |
|
1462 | + * @param \SimpleXMLElement $v |
|
1463 | + */ |
|
1412 | 1464 | protected function insertIntegerSimplexml($parent, $k, $v, $fn) |
1413 | 1465 | { |
1414 | 1466 | return $this->attachNodes($parent, \dom_import_simplexml($v), $fn); |
1415 | 1467 | } |
1416 | 1468 | |
1469 | + /** |
|
1470 | + * @param integer $k |
|
1471 | + * @param FluidXml $v |
|
1472 | + */ |
|
1417 | 1473 | protected function insertIntegerFluidxml($parent, $k, $v, $fn) |
1418 | 1474 | { |
1419 | 1475 | return $this->attachNodes($parent, $v->dom()->documentElement, $fn); |
1420 | 1476 | } |
1421 | 1477 | |
1478 | + /** |
|
1479 | + * @param integer $k |
|
1480 | + * @param FluidContext $v |
|
1481 | + */ |
|
1422 | 1482 | protected function insertIntegerFluidcontext($parent, $k, $v, $fn) |
1423 | 1483 | { |
1424 | 1484 | return $this->attachNodes($parent, $v->asArray(), $fn); |
@@ -133,8 +133,8 @@ discard block |
||
133 | 133 | class FluidXml implements FluidInterface |
134 | 134 | { |
135 | 135 | use NewableTrait, |
136 | - ReservedCallTrait, // For compatibility with PHP 5.6. |
|
137 | - ReservedCallStaticTrait; // For compatibility with PHP 5.6. |
|
136 | + ReservedCallTrait, // For compatibility with PHP 5.6. |
|
137 | + ReservedCallStaticTrait; // For compatibility with PHP 5.6. |
|
138 | 138 | |
139 | 139 | const ROOT_NODE = 'doc'; |
140 | 140 | |
@@ -168,9 +168,9 @@ discard block |
||
168 | 168 | $doc = $this->document; |
169 | 169 | |
170 | 170 | $defaults = [ 'root' => self::ROOT_NODE, |
171 | - 'version' => '1.0', |
|
172 | - 'encoding' => 'UTF-8', |
|
173 | - 'stylesheet' => null ]; |
|
171 | + 'version' => '1.0', |
|
172 | + 'encoding' => 'UTF-8', |
|
173 | + 'stylesheet' => null ]; |
|
174 | 174 | |
175 | 175 | if (\is_string($root)) { |
176 | 176 | // The root option can be specified as first argument |
@@ -196,9 +196,9 @@ discard block |
||
196 | 196 | |
197 | 197 | if (! empty($opts['stylesheet'])) { |
198 | 198 | $attrs = 'type="text/xsl" ' |
199 | - . "encoding=\"{$opts['encoding']}\" " |
|
200 | - . 'indent="yes" ' |
|
201 | - . "href=\"{$opts['stylesheet']}\""; |
|
199 | + . "encoding=\"{$opts['encoding']}\" " |
|
200 | + . 'indent="yes" ' |
|
201 | + . "href=\"{$opts['stylesheet']}\""; |
|
202 | 202 | $stylesheet = new \DOMProcessingInstruction('xml-stylesheet', $attrs); |
203 | 203 | |
204 | 204 | $doc->dom->insertBefore($stylesheet, $doc->dom->documentElement); |
@@ -447,8 +447,8 @@ discard block |
||
447 | 447 | const MODE_EXPLICIT = 1; |
448 | 448 | |
449 | 449 | private $config = [ self::ID => '', |
450 | - self::URI => '', |
|
451 | - self::MODE => self::MODE_EXPLICIT ]; |
|
450 | + self::URI => '', |
|
451 | + self::MODE => self::MODE_EXPLICIT ]; |
|
452 | 452 | |
453 | 453 | public function __construct($id, $uri, $mode = 1) |
454 | 454 | { |
@@ -529,7 +529,6 @@ discard block |
||
529 | 529 | * |
530 | 530 | * ```php |
531 | 531 | * $xml = fluidxml(); |
532 | - |
|
533 | 532 | * $xml->query("/doc/book[@id='123']"); |
534 | 533 | * |
535 | 534 | * // Relative queries are valid. |
@@ -549,7 +548,6 @@ discard block |
||
549 | 548 | * |
550 | 549 | * ```php |
551 | 550 | * $xml = fluidxml(); |
552 | - |
|
553 | 551 | * $xml->appendChild('title', 'The Theory Of Everything'); |
554 | 552 | * $xml->appendChild([ 'author' => 'S. Hawking' ]); |
555 | 553 | * |
@@ -664,8 +662,8 @@ discard block |
||
664 | 662 | class FluidContext implements FluidInterface, \ArrayAccess, \Iterator |
665 | 663 | { |
666 | 664 | use NewableTrait, |
667 | - ReservedCallTrait, // For compatibility with PHP 5.6. |
|
668 | - ReservedCallStaticTrait; // For compatibility with PHP 5.6. |
|
665 | + ReservedCallTrait, // For compatibility with PHP 5.6. |
|
666 | + ReservedCallStaticTrait; // For compatibility with PHP 5.6. |
|
669 | 667 | |
670 | 668 | private $document; |
671 | 669 | private $nodes = []; |
@@ -133,8 +133,8 @@ discard block |
||
133 | 133 | class FluidXml implements FluidInterface |
134 | 134 | { |
135 | 135 | use NewableTrait, |
136 | - ReservedCallTrait, // For compatibility with PHP 5.6. |
|
137 | - ReservedCallStaticTrait; // For compatibility with PHP 5.6. |
|
136 | + ReservedCallTrait, // For compatibility with PHP 5.6. |
|
137 | + ReservedCallStaticTrait; // For compatibility with PHP 5.6. |
|
138 | 138 | |
139 | 139 | const ROOT_NODE = 'doc'; |
140 | 140 | |
@@ -142,7 +142,7 @@ discard block |
||
142 | 142 | |
143 | 143 | public static function load($document) |
144 | 144 | { |
145 | - if (\is_string($document) && ! is_an_xml_string($document)) { |
|
145 | + if (\is_string($document) && !is_an_xml_string($document)) { |
|
146 | 146 | // Removes any empty new line at the beginning, |
147 | 147 | // otherwise the first character check fails. |
148 | 148 | |
@@ -154,7 +154,7 @@ discard block |
||
154 | 154 | $document = \file_get_contents($file); |
155 | 155 | } |
156 | 156 | |
157 | - if (! $is_file || ! $is_readable || ! $document) { |
|
157 | + if (!$is_file || !$is_readable || !$document) { |
|
158 | 158 | throw new \Exception("File '$file' not accessible."); |
159 | 159 | } |
160 | 160 | } |
@@ -167,10 +167,10 @@ discard block |
||
167 | 167 | $this->document = new FluidDocument(); |
168 | 168 | $doc = $this->document; |
169 | 169 | |
170 | - $defaults = [ 'root' => self::ROOT_NODE, |
|
170 | + $defaults = ['root' => self::ROOT_NODE, |
|
171 | 171 | 'version' => '1.0', |
172 | 172 | 'encoding' => 'UTF-8', |
173 | - 'stylesheet' => null ]; |
|
173 | + 'stylesheet' => null]; |
|
174 | 174 | |
175 | 175 | if (\is_string($root)) { |
176 | 176 | // The root option can be specified as first argument |
@@ -190,11 +190,11 @@ discard block |
||
190 | 190 | |
191 | 191 | $doc->xpath = new \DOMXPath($doc->dom); |
192 | 192 | |
193 | - if (! empty($opts['root'])) { |
|
193 | + if (!empty($opts['root'])) { |
|
194 | 194 | $this->appendSibling($opts['root']); |
195 | 195 | } |
196 | 196 | |
197 | - if (! empty($opts['stylesheet'])) { |
|
197 | + if (!empty($opts['stylesheet'])) { |
|
198 | 198 | $attrs = 'type="text/xsl" ' |
199 | 199 | . "encoding=\"{$opts['encoding']}\" " |
200 | 200 | . 'indent="yes" ' |
@@ -232,7 +232,7 @@ discard block |
||
232 | 232 | $namespaces = []; |
233 | 233 | |
234 | 234 | if (\is_string($arguments[0])) { |
235 | - $args = [ $arguments[0], $arguments[1] ]; |
|
235 | + $args = [$arguments[0], $arguments[1]]; |
|
236 | 236 | |
237 | 237 | if (isset($arguments[2])) { |
238 | 238 | $args[] = $arguments[2]; |
@@ -439,16 +439,16 @@ discard block |
||
439 | 439 | |
440 | 440 | class FluidNamespace |
441 | 441 | { |
442 | - const ID = 'id' ; |
|
443 | - const URI = 'uri' ; |
|
442 | + const ID = 'id'; |
|
443 | + const URI = 'uri'; |
|
444 | 444 | const MODE = 'mode'; |
445 | 445 | |
446 | 446 | const MODE_IMPLICIT = 0; |
447 | 447 | const MODE_EXPLICIT = 1; |
448 | 448 | |
449 | - private $config = [ self::ID => '', |
|
449 | + private $config = [self::ID => '', |
|
450 | 450 | self::URI => '', |
451 | - self::MODE => self::MODE_EXPLICIT ]; |
|
451 | + self::MODE => self::MODE_EXPLICIT]; |
|
452 | 452 | |
453 | 453 | public function __construct($id, $uri, $mode = 1) |
454 | 454 | { |
@@ -664,8 +664,8 @@ discard block |
||
664 | 664 | class FluidContext implements FluidInterface, \ArrayAccess, \Iterator |
665 | 665 | { |
666 | 666 | use NewableTrait, |
667 | - ReservedCallTrait, // For compatibility with PHP 5.6. |
|
668 | - ReservedCallStaticTrait; // For compatibility with PHP 5.6. |
|
667 | + ReservedCallTrait, // For compatibility with PHP 5.6. |
|
668 | + ReservedCallStaticTrait; // For compatibility with PHP 5.6. |
|
669 | 669 | |
670 | 670 | private $document; |
671 | 671 | private $nodes = []; |
@@ -675,14 +675,14 @@ discard block |
||
675 | 675 | { |
676 | 676 | $this->document = $document; |
677 | 677 | |
678 | - if (! \is_array($context) && ! $context instanceof \Traversable) { |
|
678 | + if (!\is_array($context) && !$context instanceof \Traversable) { |
|
679 | 679 | // DOMDocument, DOMElement and DOMNode are not iterable. |
680 | 680 | // DOMNodeList and FluidContext are iterable. |
681 | - $context = [ $context ]; |
|
681 | + $context = [$context]; |
|
682 | 682 | } |
683 | 683 | |
684 | 684 | foreach ($context as $n) { |
685 | - if (! $n instanceof \DOMNode) { |
|
685 | + if (!$n instanceof \DOMNode) { |
|
686 | 686 | throw new \Exception('Node type not recognized.'); |
687 | 687 | } |
688 | 688 | |
@@ -808,7 +808,7 @@ discard block |
||
808 | 808 | } |
809 | 809 | } |
810 | 810 | |
811 | - if (! $found) { |
|
811 | + if (!$found) { |
|
812 | 812 | $unique_results[] = $r; |
813 | 813 | } |
814 | 814 | } |
@@ -921,7 +921,7 @@ discard block |
||
921 | 921 | // the user has passed two arguments: |
922 | 922 | // 1. is the attribute name |
923 | 923 | // 2. is the attribute value |
924 | - if (! \is_array($arguments[0])) { |
|
924 | + if (!\is_array($arguments[0])) { |
|
925 | 925 | $attrs = [$arguments[0] => $arguments[1]]; |
926 | 926 | } |
927 | 927 | |
@@ -1045,8 +1045,8 @@ discard block |
||
1045 | 1045 | |
1046 | 1046 | protected function handleOptionals($element, &$optionals) |
1047 | 1047 | { |
1048 | - if (! \is_array($element)) { |
|
1049 | - $element = [ $element ]; |
|
1048 | + if (!\is_array($element)) { |
|
1049 | + $element = [$element]; |
|
1050 | 1050 | } |
1051 | 1051 | |
1052 | 1052 | $switch_context = false; |
@@ -1069,7 +1069,7 @@ discard block |
||
1069 | 1069 | } |
1070 | 1070 | } |
1071 | 1071 | |
1072 | - return [ $element, $attributes, $switch_context ]; |
|
1072 | + return [$element, $attributes, $switch_context]; |
|
1073 | 1073 | } |
1074 | 1074 | |
1075 | 1075 | protected function insertElement($element, &$optionals, $fn) |
@@ -1093,7 +1093,7 @@ discard block |
||
1093 | 1093 | // offers to the user and is the same of: |
1094 | 1094 | // 1. appending a child switching the context |
1095 | 1095 | // 2. setting the attributes over the new context. |
1096 | - if (! empty($attributes)) { |
|
1096 | + if (!empty($attributes)) { |
|
1097 | 1097 | $new_context->setAttribute($attributes); |
1098 | 1098 | } |
1099 | 1099 | |
@@ -1121,9 +1121,9 @@ discard block |
||
1121 | 1121 | $v_is_string = \is_string($v); |
1122 | 1122 | $v_is_xml = $v_is_string && is_an_xml_string($v); |
1123 | 1123 | $k_is_special = $k_is_string && $k[0] === '@'; |
1124 | - $k_isnt_special = ! $k_is_special; |
|
1125 | - $v_isnt_string = ! $v_is_string; |
|
1126 | - $v_isnt_xml = ! $v_is_xml; |
|
1124 | + $k_isnt_special = !$k_is_special; |
|
1125 | + $v_isnt_string = !$v_is_string; |
|
1126 | + $v_isnt_xml = !$v_is_xml; |
|
1127 | 1127 | /////////////////////////////////////////////////////// |
1128 | 1128 | |
1129 | 1129 | if ($k_is_string && $k_isnt_special && $v_is_string && $v_isnt_xml) { |
@@ -1143,7 +1143,7 @@ discard block |
||
1143 | 1143 | } |
1144 | 1144 | |
1145 | 1145 | ///////////////////////////////////////////////////// |
1146 | - $k_is_special_a = $k_is_special && ! $k_is_special_c; |
|
1146 | + $k_is_special_a = $k_is_special && !$k_is_special_c; |
|
1147 | 1147 | ///////////////////////////////////////////////////// |
1148 | 1148 | |
1149 | 1149 | if ($k_is_special_a && $v_is_string) { |
@@ -1195,7 +1195,7 @@ discard block |
||
1195 | 1195 | $v_is_domnode = $v instanceof \DOMNode; |
1196 | 1196 | /////////////////////////////////////// |
1197 | 1197 | |
1198 | - if ($k_is_integer && ! $v_is_domdoc && $v_is_domnode) { |
|
1198 | + if ($k_is_integer && !$v_is_domdoc && $v_is_domnode) { |
|
1199 | 1199 | return $this->insertIntegerDomnode($parent, $k, $v, $fn, $optionals); |
1200 | 1200 | } |
1201 | 1201 | |
@@ -1263,15 +1263,15 @@ discard block |
||
1263 | 1263 | |
1264 | 1264 | protected function attachNodes($parent, $nodes, $fn) |
1265 | 1265 | { |
1266 | - if (! \is_array($nodes) && ! $nodes instanceof \Traversable) { |
|
1267 | - $nodes = [ $nodes ]; |
|
1266 | + if (!\is_array($nodes) && !$nodes instanceof \Traversable) { |
|
1267 | + $nodes = [$nodes]; |
|
1268 | 1268 | } |
1269 | 1269 | |
1270 | 1270 | $context = []; |
1271 | 1271 | |
1272 | 1272 | foreach ($nodes as $el) { |
1273 | 1273 | $el = $this->document->dom->importNode($el, true); |
1274 | - $context[] = $fn( $parent, $el); |
|
1274 | + $context[] = $fn($parent, $el); |
|
1275 | 1275 | } |
1276 | 1276 | |
1277 | 1277 | return $context; |
@@ -1313,7 +1313,7 @@ discard block |
||
1313 | 1313 | $el = $this->createElement($k, $v); |
1314 | 1314 | $el = $fn($parent, $el); |
1315 | 1315 | |
1316 | - return [ $el ]; |
|
1316 | + return [$el]; |
|
1317 | 1317 | } |
1318 | 1318 | |
1319 | 1319 | protected function insertStringMixed($parent, $k, $v, $fn, &$optionals) |
@@ -1329,7 +1329,7 @@ discard block |
||
1329 | 1329 | // they are supplied, so 'appendChild' is the perfect operation. |
1330 | 1330 | $this->newContext($el)->appendChild($v, ...$optionals); |
1331 | 1331 | |
1332 | - return [ $el ]; |
|
1332 | + return [$el]; |
|
1333 | 1333 | } |
1334 | 1334 | |
1335 | 1335 | protected function insertIntegerArray($parent, $k, $v, $fn, &$optionals) |
@@ -1356,7 +1356,7 @@ discard block |
||
1356 | 1356 | $el = $this->createElement($v); |
1357 | 1357 | $el = $fn($parent, $el); |
1358 | 1358 | |
1359 | - return [ $el ]; |
|
1359 | + return [$el]; |
|
1360 | 1360 | } |
1361 | 1361 | |
1362 | 1362 | protected function insertIntegerXml($parent, $k, $v, $fn) |