Phoneappli Advent Calender 2020 の6日目
昨日の@teino_ponytailさんの「HTTP/3ってなに?」を見て、
「あ、Advent Calendarってそういう感じでやるんだ」
と初めて知った、ITリテラシーの低い人です。
仕事でウェブ系開発やるようになって、PHPもやらされるようになりました。
PHPってwebの情報多いし、コピペで簡単に動くものが作れるし、適度に枯れた、
素晴らしい言語だと思います。
「でも、コピペでお金もらうのも仕事としてどうかな」
と思ったりして、言語仕様を調べたこともありました。
その時にJavaエンジニア視点で、PHPのおかしなところとか、
知らんかったことがけっこうあったので、そういうところをまとめました。
まとめたのが2018年なんで、今のPHP7には対応してないかもしれません。
ちなみに、この会社でPHP使うことは99%無いと思います。
私もWordpressいじるとき以外、一生使わないと思います。
PHPのコンパイルと実行
PHPは実行時に、オペコードと呼ばれる中間コードにコンパイルされ、
オペコードを実行マシンが実行します。
レキサー(Lexer):構文解析を行い、トークンに分解する。
パーサー(Parser):トークンをぱーすし、オペコードへ変換を行う。
PHPプログラム→レキサー→トークン生成→パーサー→オペコード生成→オペコード実行。
生成オペコードをキャッシュするオペコードキャッシング(Alternative PHP Cache = APC)
という追加実装もあります。
終了タグの省略
の終了タグ。多くの場合、終了タグを省略することを推奨しています。
ほかのファイルから読み込まれた際に、改行などが出力されてしまう影響を防ぎます。
タグを閉じないのが普通なんてA型の人とか発狂しそう。
変数スコープの拡張
ローカルスコープ内からグローバル変数へアクセスしたい場合、globalキーワードが必要。
関数内で単にグローバル変数名で呼んでも、ローカル変数として扱われてしまいます。
$foo = 1;
function some_func()
{
global $foo;
echo $foo, PHP_EOL;
}
定数の定義方法
define()(古い)とconst(新しい)の2種類があり、constは名前空間の影響を受けます。
基本的にはconst推奨。
マジック定数
PHPが自動的に定義する定数があります。
よく使うのは、__FILE__
とか__DIR___
など。
テストコードでは、__CLASS__
とか__METHOD__
など。
何が入ってるかはお楽しみ♡
三項演算子
ポイントは左側から展開されること。Javaは右側から展開されます。
ネストしての使用は非推奨。
<?php
$flag1 = true;
$flag2 = false;
echo $flag1 ? 1 : $flag2 ? 2 :0;
echo ( ( $flag1 ? 1 : $flag2) ? 2 : 0);
エラー制御演算子
@を付与すると、その式で発生するエラーが抑止されます。
デバッグが難しくなるからあんまり使わないけど、
「とにかく明日までに直せ」って言われたら使っちゃうやつ。
<?php
$var = @$_GET['foo'];
配列の宣言は最後にカンマをつけておく
なんでみんなこうしてるのか諸説ありますが、PHPのマニュアルに
「最後にカンマつけてもOK」
って仕様として定めているからだと思われます。
<?php
$fruits = array (
'apple',
'banana',
'melon',
);
配列の演算
普通の配列や連想配列をそのまま比較、結合などができます。
+ 結合
== 比較
=== 同一比較
!=, <> 比較
!== 同一比較
配列の結合に演算子を用いると、左辺に同じキーがあった場合に上書きしません。
(=何も起こらない)
array_merge関数で結合すると上書きされるので、使い分ける必要あり。
配列のキーの存在可否判定
array_key_exists()は全項走査、isset()は単一チェック。
array_key_exists()はキーが存在すればtrueですが、
issetの場合はキーが存在してさらに値がnullでない場合にtrueになるので注意が必要。
issetのほうが速いけど、確実性を求めるならarray_key_exists()。
switch文
複数条件を重ねて書けます。地味に便利。
<?php
switch ($some) {
case '1':
echo 'one';
break;
case '2':
case '3':
echo '2 or 3';
break;
default:
echo 'other';
break;
}
require, require_oneceと、include, include_onceとの違い
include系は、ファイルが存在しなかった場合にErrorにならずwarningとなります。
みんなコピペだから意外に知られていない気がする。
タイプヒンティング
関数の引数の前に型を記載すると、誤った場合にCatchable Fatal Errorを出してくれます。
$mylist = ['A', 'B'];
function typehintTest(array $list) // <--- タイプヒンティング
{
echo $list;
}
呼出例:
typehintTest($myList); // <--- OK
typehintTest('C'); // Catchable fatal error: Argument 1 passed to typehintTest() must be of the type array, string given
コールバック関数
関数の引数に「別の関数または別の関数名の文字列」を与えることのできる関数のこと。
AJaxで使ったアレ。
function greeting(callable $method)
{
echo $method(); //引数に与えられた関数を呼び出す
}
function helloworld()
{
echo "Hello World!!!!";
}
greeting("helloworld");
Hello World!!!!
無名関数とクロージャ
あんまり好きじゃないやつだけど仕様として存在します。
<?php
$my_pow = function($times = 2)
{
return function ($v) use (&$times)
{
return pow($v, $times);
};
};
関数と言語構造の違い
array(), isset(), unset(), empty(), list() ,eval(), exit()
一見すると関数に見えるんですけど実は言語構造。PHPの特徴的仕様。
関数ではないのでコールバックや変数への代入はできませんが、
そういうこと普段あんまりしないんで、知らなくても問題ないです。
宣言していない(未定義)のプロパティへアクセスできる
プロパティ(メンバ変数)を宣言していなくても後から追加できます。
賛否両論あります。個人的に嫌なところ。
<?php
$yamada = new Employee();
$yamada->job = 'プログラマ';
コンストラクタとデストラクタ
__construct
, __destruct
メソッドが存在します。
PHP4ではクラス名のメソッドがコンストラクタの役割をしていましたが、
PHP5から___construct
が正しいです。
両方あるとE_STRICTエラーになる罠。
さらにPHP7では___construct
がない場合にE_DEPRECATEDが発生。
空の原子クラスはstdClass
JavaでいうところのClass。
スカラをオブジェクトにキャストするとstdClassになり、
スカラ値はscalarというプロパティに格納されているらしい。
格納されてるとこ見たことないけど。
マジックメソッド
__get, __set, __construct, _cloneなどがあります。
必要に応じてオーバーライドできます。javaのmainみたいなやつ。
遅延的束縛(static::)
親クラスでself::hello();としたメソッドを、継承した子クラスで呼んでも、
(オーバーライドしていても)親クラスのほうのメソッドが呼ばれます。
実行コンテキストは親クラス、ということらしい。よくしらんけど。
<?php
class Foo {
public function helloGw()
{ self::hello(); }; // => static::hello()
public static function hello()
{ echo __CLASS__; }
}
class Bar extends Foo {
public static function hello()
{ echo __CLASS__; }
}
$bar = new Bar();
$bar->helloGw(); // Fooが出力される
名前空間
Javaでいうところのパッケージ名。大文字小文字を区別しません。
一つのファイルに複数設定できますが、影響範囲を間違えないよう{}で囲むこと推奨。
namespace mynamespace {
function getGreeting() {
return 'こんにちわ';
}
}
定義済みの例外
Exception
ErrorException
LogicException
BadFunctionCallException
badMethodCallException
DomainException
InvalidArgumentException
「オブジェクトはデフォルトでは参照渡しとなります」問題
関数の引数は値渡しですし、何も意識しなければ基本的には値渡しです。
しかし、オブジェクトを変数に代入したりコピーをすると、参照渡しに見えます。
「オブジェクトはデフォルトでは参照渡しとなります」というマニュアルの記述から
いろいろ誤解が生まれているようです。
<?php
class A {
public $foo = 1;
}
$a = new A;
$b = $a; // $a と $b は同じ ID を持つコピーです
// ($a) = ($b) = <id>
$b->foo = 2;
echo $a->foo."\n";
2
配列への代入は常にコピー
PHP Manualより
配列への代入においては、常に値がコピーされることに注意してください。配列をリファレンスでコピーする場合には、 リファレンス演算子を使う必要があります。
変数のリファレンスとコピーオンライト
変数の代入は値渡しですが、PHP5から内部的には1回だけの参照渡し
(コピーオンライト方式/COW)が行われるようになりました。
PHP7ではNULL,真偽値,整数,浮動小数点数の型だけはスカラー値がコピーされ、
より高速化されたらしい。知らなくてもぜんぜん問題ないと思うけど。
そういえば、PHP8が11月26日リリースされてたそうな。
PHPの原型「Personal Home Page Tools」が1995年に登場して25年周年だそうです。