PHP「関数っぽいもの」列伝

  • 10
    いいね
  • 0
    コメント

これは


お前誰よ

  • うさみけんた / GitHub: zonuexe
  • ピクシブ株式会社 (東京)
    • 福岡オフィスできました!
    • CTOが赴任して週明けから稼動するよ
  • PhpStorm普及業の傍らEmacsのphp-mode開発

このスライドはQiitaで公開済みなので、PHPについて知りたい型はゆっくり読んでね

せっかくなので自薦のPHP記事を置いておきますね


さて


PHPの世界観


PHPの世界観

  • 関数
     
  • 言語構造(構文)

PHPの世界観

  • 関数
    • PHPの世界から定義できる
  • 言語構造(構文)
    • (PHPの世界からは)定義できない
    • (C拡張で介入できるけど今回は触れない)

よくある質問

  • Q. issetis_null()ってどう違うの?
  • Q. issetarray_key_exists()ってどう違うの?

よくある質問

  • Q. issetis_null()ってどう違うの?
  • Q. issetarray_key_exists()ってどう違うの?

A. 違うよ、全然違うよ


関数

  • 算数で習ったのは $f(x)=x\times2$ みたいなの
  • PHP的には$f = function($x) { return $x * 2; };
    • ↑引数が同じなら絶対に同じ値をreturnする

関数

  • 算数で習ったのは $f(x)=x\times2$ みたいなの
  • PHP的には$f = function($x) { return $x * 2; };
    • ↑引数が同じなら絶対に同じ値をreturnする

余談

  • PHP(とか他言語)の関数は、そうとは限らない
  • 例) mt_rand(0, 9) は、その前提だと困る

関数のなかまたち (callable)

  • ユーザー定義関数 (文):
    • function f($a){ ... }
  • クロージャ (関数式):
    • $f = function($a) { ... };
  • メソッド:
    • クラス内 public function f($a){ ... }
  • 静的メソッド:
    • クラス内 public static function f($a){ ... }
  • オブジェクト + __invoke (マジックメソッド)
    • クラス内 public function __invoke($a){ ... }

関数のなかまたち (callable)

  • メソッドとかクロージャとかいろいろあるけど、こいつらは関数のようなもの
$f = [$obj, 'hoge']; // $obj->hoge()
$f = ['Ns\Klass', 'foo']; // Ns\Klass::foo()

$f($v);
is_callable($f); // => true

こんなこともできる

// Before
if ($v === 1) {
    $obj->hoge($v)
} else {
    Ns\Klass::foo($v)
}

// After
$f = ($v === 1) ? [$obj, 'hoge'] : ['Ns\Klass', 'foo'];
$f($v);

※ どちらが良いコードかは場面による


こんなこともできる

$ary = ["apple", "orange", "banana"];

// Before
$bry = [];
foreach ($ary as $i => $a) {
    $bry[$i] => ucfirst($a);
}

// After
$cry = array_map('ucfirst', $ary);
// => ["Apple", "Orange", "Banana"]

ここまでは「関数」と「関数っぽいフレンズ(callable)」の話


「関数っぽいけど関数ではない」ものがある


言語構造(構文)

  • 英語では language constructs
  • if ($a == $b) みたいなやつ
    • PHPの経験があればifが関数ではない
      ({}がくっついたりする)ことは明らかだが、言語仕様の知識がなければ、これは自明なことではない
  • 実際、if が言語構造(構文)ではない言語はある

関数っぽく見える言語構造

諸説あるが

array, assert, declare, die, echo, empty, eval, exit, include, include_once, isset, list, print, require, require_once, unset, ……

一見すると見分けがつかない (主観)


構文 意味
array 配列リテラル array(1, 2)
assert 表明 assert(foo() === "foo")
declare PHP処理系に特殊な命令 declare(strict_types=1)
die PHPスクリプトの終了 foo() or dir(1);
echo 文字列を標準出力 echo $message, PHP_EOL;
empty 値/変数が「空」または「偽」かの検査 if (empty($v) || empty($a['i']))
eval 文字列をPHPスクリプトとして評価 eval($s)
exit PHPスクリプトの終了 exit(0);
include
include_once
PHPスクリプトの読み込み include_once __DIR__ . '/Foo.php';
isset 値/変数が存在するかの検査 $v = isset($a['i']) ? $a['i'] : 's';
list 複数の値(配列)を変数に代入 list($a, $b, $c) = [1, 2, 3];
print 文字列を標準出力 ($c === 1) ? print $t : print $f;
require
require_once
PHPスクリプトの読み込み require 'lib.php';
unset 変数/インデックスの破棄 unset($v);
unset($a['i']);

いっぱいある!!!!


出力するやつ


echo, print

echo "fizz";
echo("buzz");

print "foo";
print("bar");

echo, print

  • デフォルトで標準出力に出力できる
  • echo は値を返さない
  • print は値を返す (関数っぽい言語構造)
  • 出力バッファリングでキャプチャできる
  • fwrite(STDOUT, ...) とは微妙に異なる ←重要

echo, print

// これはok
$v === "x" and print "foo";

// これはだめ
$v === "x" and echo "foo";
// PHP Parse error: Syntax error, unexpected T_ECHO on line 1

PHPを終了するやつ

exit, die

exit;
exit(1);

foo() or die(1);
  • 引数で渡した値は終了ステータスになる
    • 0が正常、1が異常
    • Webサーバーだと200とか500とかにマッピングされる

配列リテラル

$a = array()
$b = array("apple", "banana", "orange");
$c = array("name" => "Miku", "age" => 16);

list($a, $b) = foo();
list($a) = array("a");

// PHP 7.1から
list("foo" => $f) = ["foo" => "F"];
  • PHP 5.3までは ["apple", "banana", "orange"] って書けなかった
  • list() は関数っぽいものが代入の左辺にあって他の言語の常識があると死ぬ

ディレクティブ宣言


declare

<?php
declare(strict_types=1);
declare(ticks=1);
declare(encoding='ISO-8859-1');
  • ファイルローカルに適用される特殊な宣言
  • 関数っぽいけど declare(name=val) みたいな変な構文
  • ファイルのコンパイル時に評価される
    • 変数・定数などは利用できない

値のチェック


isset, empty

isset($v);
isset($v[0]);
isset($v, $w);

empty($v);
empty($v[0]);
  • issetは複数の値を評価できる
  • emptyはPHP 5.5までは変数しか評価できなかった

ファイルを読み込む


include, require

include("foo.php");
require("foo.php");
include_once("foo.php");
require_once("foo.php");
  • () は省略可能
  • 絶対パスではないファイル指定をすると、include_path から探索する
    • なので最近はオートローディングが主流
  • PHPをテンプレートエンジンとして利用するときは部分テンプレートを取り込める

コード辺を実行したりする

eval("foo();");
eval("\$v = foo();");

変数を消す

unset($v);
unset($v["key"]);

表明


assert

assert(foo() === 1);
assert("foo() === 1");
  • PHP 5では関数
  • PHP 7では関数でもあり言語構造でもある
    謎の存在に昇華した
    • is_callable('assert') // => true
    • おそらく互換性のため
  • 本番運用時に実行されないようにしたりできる

最後に


言語構造ではないもの(ただの関数)

// C言語の sizeof は演算子
// PHPでは、ただの関数 (countのエイリアス)
$i = sizeof($a);

$i = sizeof $a; // ← これはだめ!

三三三 ヾ(〃><)ノ゙