🌊

PHP8.0がリリースされたので新機能全部やる

2020/12/01に公開

2020/11/26にPHP8.0.0がリリースされました

ということで、UPGRADINGに載っている機能や変更点をだいたい全部見て回ることにします。

Backward Incompatible Changes

後方互換性のない変更。
なお、ここで削除される機能の多くは何年も前から公知されています

PHPコア

match is now a reserved keyword.

matchが予約語になりました。
match構文の導入に伴う措置です。

function match(){}

// PHP8.0
Parse error: syntax error, unexpected token "match", expecting "("
// PHP7.4
エラーは起こらない

Assertion failures now throw by default.

assertのデフォルト動作が例外になりました。

assert(false);

// PHP8.0
Fatal error: Uncaught AssertionError: assert(false)
// PHP7.4
Warning: assert(): assert(false) failed

PHP7.4以前と同じ動作に戻すにはini設定assert.exception=0を設定します。

ちなみにPHP7.0以降、アサート文はコンパイル時に除去されて実行コストが0になるので、気軽に突っ込んでおけます。
便利なのでどんどん使いましょう。

Removed ability to call non-static methods statically.

静的でないメソッドを静的に呼び出すことができなくなりました。

class HOGE{
    public function fuga(){}
}
HOGE::fuga();

// PHP8.0
Fatal error: Uncaught Error: Non-static method HOGE::fuga() cannot be called statically
// PHP7.4
Deprecated: Non-static method HOGE::fuga() should not be called statically

これまでは警告は出るものの実行自体はできていました。
今後は致命的エラーになります。

Removed (unset) cast.

(unset)キャストが削除されました。

$a = 1;
(unset)$a;

// PHP8.0
Fatal error: The (unset) cast is no longer supported
// PHP7.4
Deprecated: The (unset) cast is deprecated

今後は普通にunset($a);としましょう。

Removed track_errors ini directive.

ini設定track_errorsが削除されました。

これは何かというと$php_errormsgです。
track_errorsが有効になっていると、最後に発生したエラーメッセージが$php_errormsgに登録されるという謎機能です。
デフォルトはずっとオフで、またPHP7.2でE_DEPRECATEDになったので使っている人もほとんどいないでしょうが、今後はerror_get_lastを使いましょう。

Removed the ability to define case-insensitive constants.

defineの第三引数$case_insensitiveが削除されました。

define('HOGE', 'FUGA', true);
var_dump(HOGE, HoGe);

// PHP8.0
Warning: define(): Argument #3 ($case_insensitive) is ignored
// PHP7.4
FUGA, FUGA // Deprecated: define(): Declaration of case-insensitive constants is deprecated

trueにすると定数が大文字小文字を区別しなくなるという意味のわからない機能です。

PHPは大文字小文字を区別するかしないかはわりかしややこしいのですが、関数/メソッドは区別しない、それ以外は区別する、という認識でだいたい大丈夫です。
実装上は、一律して区別するものだと考えておいたほうが楽でしょう。

Access to undefined constants now always results in an Error exception.

未定義定数にアクセスすると致命的エラーが発生します。

define('HOGE', 'FUGA', true);
var_dump(HOGE, HoGe);

// PHP8.0
Fatal error: Uncaught Error: Undefined constant "HOGE"
// PHP7.4
HOGE // Warning: Use of undefined constant HOGE - assumed 'HOGE'

これまではE_WARNINGを出したうえで、定数名と同じ文字列が割り当てられていましたが、この手抜きができなくなります。

Removed ability to specify an autoloader using an __autoload() function.

__autoload関数が削除されました。

function __autoload($class){}

// PHP8.0
Fatal error: __autoload() is no longer supported, use spl_autoload_register() instead
// PHP7.4
Deprecated: __autoload() is deprecated, use spl_autoload_register() instead

削除されたといっても完全に消えたわけではなく、定義することもできないという特別扱いです。

今後はspl_autoload_registerを使いましょう。
Composerのオートロードとか使った方がもっといいですが。

Removed the $errcontext argument for custom error handlers.

set_error_handlerの第1引数$error_handlerの第5引数$errcontextが削除されました。

set_error_handler(function($errno, $errstr, $errfile, $errline, $errcontext){
    echo $errcontext['fuga'];
});

function hoge(){
    $fuga = 'piyo';
    $y = '1a' + 1;
}
hoge();

// PHP8.0
Fatal error: Uncaught ArgumentCountError: Too few arguments to function {closure}(), 4 passed and exactly 5 expected
// PHP7.4
"piyo"

エラーが発生した個所のローカル変数が配列で入ってくるという謎機能です。

Removed create_function().

PHPの魔関数のひとつcreate_functionが削除されました。

$a = create_function('$a', 'echo $a;');
$a('hoge');

// PHP8.0
Fatal error: Uncaught Error: Call to undefined function create_function
// PHP7.4
"hoge" // Deprecated: Function create_function() is deprecated

かわりに無名関数を使いましょう。
無名関数はPHP5.3から使えるので、古のソースを熟成させながら使い続けてでもいないかぎり、いまどきcreate_functionはそうそう出てこないはずです。

Removed each()

eachが削除されました。

$arr = [1, 2, 3];
while ([$key, $val] = each($arr)) {
    var_dump($key, $val);
}

// PHP8.0
Fatal error: Uncaught Error: Call to undefined function each()
// PHP7.4
0,1 1,2 2,3 // Deprecated: The each() function is deprecated

この関数を使う意味は全くないので、普通にforeach使いましょう

Removed ability to unbind $this from closures

クロージャ内で$thisのバインドを外すことができなくなりました。
とあるのだけどよくわかりません。

$closure = Closure::bind(null, new stdClass());

// PHP8.0
Fatal error: Uncaught TypeError: Closure::bind(): Argument #1 ($closure) must be of type Closure, null given
// PHP7.4
Warning: Closure::bind() expects parameter 1 to be Closure, null given

これは何の意味があるんだ?

Removed ability to use array_key_exists() with objects

オブジェクトに対してarray_key_existsを使えなくなりました。

array_key_exists(1, (object)[0, 1]);

// PHP8.0
Fatal error: Uncaught TypeError: array_key_exists(): Argument #2 ($array) must be of type array, stdClass given
// PHP7.4
Deprecated: array_key_exists(): Using array_key_exists() on objects is deprecated

property_existsを使えってずっと前から言われていたので、今さらこれに引っかかることはないでしょう。

Made the behavior of array_key_exists() regarding the type of the key parameter consistent with isset() and normal array access.

array_key_existsの引数のキー値の挙動が、issetなどと同じになりました。

array_key_exists([], [1]);

// PHP8.0
Fatal error: Uncaught TypeError: Illegal offset type
// PHP7.4
Warning: array_key_exists(): The first argument should be either a string or an integer

isset([], [1]); // こちらは昔からFatal error

これまでは引数のキー値に配列やオブジェクトなど不正な値を突っ込むことができましたが(当然falseなので意味はない)、今後はissetなどと同様のチェックが入ります。

negative_array_index

配列の自動採番キーがマイナスに対応します。

$a = [-10=>1];
$a[] = 2;

// PHP8.0
$a[-9]=>2になる
// PHP7.4
$a[0]=>2になる

これは正直、どうして賛成多数なのかわからない。

The default error_reporting level is now E_ALL.

ini設定error_reportingのデフォルトがE_ALLになります。
これまではE_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATEDでした。

この変更により、適当に書いてるコードは動かなくなります。

echo $a;

// PHP8.0
Warning: Undefined variable $a
// PHP7.4
何も出ない

PHP7.4で何も出ないのは単にデフォルトが非表示になっているだけなので、E_ALLに設定変更すればエラーが出ます。

もちろんerror_reportingを変更すれば黙殺することもできますが、やめましょう。

display_startup_errors is now enabled by default.

ini設定display_startup_errorsの初期値が音になりました。

Using "parent" inside a class that has no parent will now result in a fatal compile-time error.

親クラスの無いクラスでparentを書くとコンパイルエラーになります。

class A{
  public function __construct(){
    parent::__construct();
  }
}

// PHP8.0
Fatal error: Cannot use "parent" when current class scope has no parent
// PHP7.4
Deprecated: Cannot use "parent" when current class scope has no parent

PHP7.4では、呼ばれないかぎりは警告にとどまります。
もちろんnew A()を実行したら、parentが存在しないため致命的エラーになります。

The @ operator will no longer silence fatal errors

エラー制御演算子@が致命的エラーには効かなくなりました。

@require("notfound.php");

// PHP8.0
Fatal error: Uncaught Error: Failed opening required 'notfound.php'
Process exited with code 255.
// PHP7.4
Process exited with code 255.

もっとも致命的エラーが起きたらスクリプトは強制終了されるので、結果は特に変わりません。

Following the hash comment operator # immediately with an opening bracket is not supported as a comment

#[はコメントにならなくなりました。

#[ これはコメント

// PHP8.0
Parse error: Unclosed '[' 
// PHP7.4
エラーは起きない

アトリビュートの実装に伴う変更です。
[以外の文字はこれまでどおりコメントです。
空白を入れた# [もコメントになります。

LSP violations will now always generate a fatal error

LSP違反となる継承は常に致命的エラーを発生します。

class C1 {
    public function method(array $a) {}
}
class C2 extends C1 {
    public function method(int $a) {}
}

// PHP8.0
Fatal error: Declaration of C2::method(int $a) must be compatible with C1::method(array $a)
// PHP7.4
Warning: Declaration of C2::method(int $a) should be compatible with C1::method(array $a)

これまでは場合によってE_FATALだったりE_WARNINGだったりしていたので、これを揃えたものです。

The precedence of the concatenation operator has changed

連結演算子の優先度が変更されます。

$errors_count = 0;
echo 'unknown_upgrade_error_' . $errors_count + 1;

// PHP8.0
unknown_upgrade_error_1
// PHP7.4
1

これまで.+の優先順位が同じで前から順だったため、直感的ではない結果になることがありました。
今後は.の優先順位が下がります。

Arguments with a default value that resolves to null at runtime will no longer implicitly mark the argument type as nullable

引数のデフォルト値にnullが指定されている場合、引数の型は暗黙的にnull許容型になります。

function test(int $arg = null){}
test(null);

型引数はintですが、デフォルト値にnullが指定されているので暗黙的に?int型となります。
このようにnullを直接渡した場合の挙動はPHP8でも変わりません。

しかし、デフォルト値を定数などで間接的に渡した場合は暗黙のnull許容型が許されなくなります。

const CONST_RESOLVING_TO_NULL = null;
function test(int $arg = CONST_RESOLVING_TO_NULL){}
test(null);

// PHP8.0
Fatal error: Uncaught TypeError: test(): Argument #1 ($arg) must be of type int, null given
// PHP7.4
エラーは出ない

一件ややこしいですが、まあnull許容型の意味を考えれば妥当な変更かなと。

A number of warnings have been converted into Error exceptions

多くのE_NOTICEがE_WARNINGに、多くのE_WARNINGが例外にと、警告レベルが厳しくなります

$a = new stdClass();
$a->b;

// PHP8.0
Warning: Undefined property: stdClass::$b
// PHP7.4
Notice: Undefined property: stdClass::$b


$a = 1;
$a->b++;

// PHP8.0
Fatal error: Uncaught Error: Attempt to increment/decrement property "b"
// PHP7.4
Warning: Attempt to increment/decrement property 'b' of non-object

横着せずに正しいコードを書くようにしましょう。

Attempting to assign multiple bytes to a string offset will now emit a warning

マルチバイト文字列にオフセットアクセスするとE_WARNINGが出るようになります。

<?php
$str = 'あいうえお';
$str[2] = 'か';

// PHP8.0
Warning: Only the first byte will be assigned to the string offset
// PHP7.4
何も出ない

なんにしろ結果は文字化けするので、このようなコードを書いている時点でおそらくバグでしょう。

Unexpected characters in source files will now result in a ParseError

ヌルバイトなど、ソースコード中に想定外の文字コードが入っているとパースエラーになります。
これまではE_COMPILE_WARNINGでした。

Uncaught exceptions now go through "clean shutdown"

"destructors will be called after an uncaught exception"、すなわちキャッチされない例外が発生してもデストラクタが呼ばれるようになる、と読めるのですが、でも実際は前から呼ばれてるんですよね。
なんのことかよくわかりません。

class HOGE{
    public function fuga(){
        throw new \Exception();
    }
    
    public function __destruct(){
        echo 'destruct called.';
    }
}
(new HOGE())->fuga();

// PHP8も7.4も
destruct called.
Fatal error: Uncaught Exception

Compile time fatal error "Only variables can be passed by reference" has been delayed until runtime

参照渡し引数に値を渡した際の警告タイミングが変更になりました。

try{
  array_pop([1,2,3]);
}catch(\Throwable $e){
  echo $e->getMessage();
}

// PHP8.0
array_pop(): Argument #1 ($array) cannot be passed by reference
// PHP7.4
Fatal error: Only variables can be passed by reference

PHP7.4まではコンパイル時にエラーが出ていたので、プログラム側で制御することができませんでした。
PHP8では評価が実行時まで遅延されるので、結果としてcatchで受け取ることができるようになります。
まあソースコードを修正すべき内容なので、catchできても特に意味はなさそうですが。

Some "Only variables should be passed by reference" notices have been converted to "Argument cannot be passed by reference" exception.

一部の参照渡し引数に値を渡した時の挙動がE_NOTICEから例外になりました。
とあるのですが実例を見つけられませんでした。

array_pop([1,2,3]); // Fatal error
array_pop(array_values([1,2,3])); // Notice: Only variables should be passed by reference

PHP7のころから、値を直接渡した場合はFatal errorとなり、別の関数の返り値をそのまま渡すとE_NOTICEになります。

The generated name for anonymous classes has changed

匿名クラスの名前が変更になりました。

var_dump(new class extends stdClass{});

// PHP8.0
object(stdClass@anonymous)
// PHP7.4
object(class@anonymous)

名前に親クラスやインターフェイスの名前が付くようになり、わかりやすくなります。
しかし、これのせいで互換性が失われる実装って想像もつきませんな。

Non-absolute trait method references in trait alias adaptations are now required to be unambiguous

トレイトに同じ名前がある場合、曖昧さを消さなければならなくなりました。

trait T1{
    public function foo(){}
}
trait T2{
    public function foo(){}
}

class X{
    use T1, T2{
        foo AS bar;
    }
    public function foo(){}
}

// PHP8.0
Fatal error: An alias was defined for method foo(), which exists in both T1 and T2
// PHP7.4
何もエラー出ない

X::foo()はどれを指すのかが曖昧ですが、PHP7.4では暗黙的にT1::foo()とみなされていました。
PHP8.0では以下のように明示しなければなりません。

class X{
    use T1, T2{
        T1::foo AS bar;
    }
}

The signature of abstract methods defined in traits is now checked against the implementing class method

traitに書いた抽象メソッドを実装したときにシグネチャを正しく解析するようになりました。

trait MyTrait {
  abstract public function neededByTrait(): string;
}

class MyClass {
  use MyTrait;
  public function neededByTrait(): int { return 42; }
}

// PHP8.0
Fatal error: Declaration of MyClass::neededByTrait() must be compatible with MyTrait::neededByTrait()
// PHP7.4
何もエラー出ない

今まで異なるシグネチャで実装できていたのがバグなのではという気がしないでもない。

Disabled functions are now treated exactly like non-existent functions

無効化した関数が存在しない関数と同じ扱いになりました。

具体的には、php.inidisable_functions ="exec"と設定したときの結果が異なります。

exec('ls');

// PHP8.0
Fatal error: Uncaught Error: Call to undefined function exec()
// PHP7.4
PHP Warning:  exec() has been disabled for security reasons

PHP7.4までのdisable_functionsは、『存在はしているけど無効化されている』という扱いでした。
PHP8においては単純に『存在しない』となるので、別の定義を行うことも可能です。

data: stream wrappers are no longer writable

dataストリームラッパーに書き込みできなくなりました。

The arithmetic and bitwise operators will now consistently throw a TypeError

各種演算子が、引数に配列、リソース、オブジェクトが渡された場合にTypeErrorを出すようになりました。
対象の演算子は+-*/**%<<>>&|^~++--です。

new stdClass() + 1;

// PHP8.0
Fatal error:  Uncaught TypeError: Unsupported operand types: stdClass + int
// PHP7.4
Notice:  Object of class stdClass could not be converted to number

オブジェクトの場合、GMPのように演算子オーバーロードが設定されている一部のクラスはこれまでどおり計算可能です。

Float to string casting will now always behave locale-independently

浮動小数から文字列へのキャストがロケールに依存しなくなりました。

setlocale(LC_ALL, 'fr-FR');
echo (string)3.14;

// PHP8.0
3.14
// PHP7.4
3,14

フランス語は小数点が','なので、これまでは3,14になっていました。
しかし(float)"3,14"は解釈できず元に戻せなかったりと、わりと中途半端な機能だったので、ロケールに関わらず常に3.14にするようになりました。

Removed support for deprecated curly braces for offset access

中括弧による文字列オフセットアクセスが削除されました。

echo '12345'{3};

// PHP8.0
Fatal error:  Array and string offset access syntax with curly braces is no longer supported
// PHP7.4
4 Deprecated:  Array and string offset access syntax with curly braces is deprecated

何のために存在したのかよくわからない機能です。

Applying the final modifier on a private method will now produce a warning unless that method is the constructor

コンストラクタ以外のprivateメソッドにfinalを書けなくなりました。

class FOO
{
  final private function bar(){}
}

// PHP8.0
Warning:  Private methods cannot be final as they are never overridden by other classes
// PHP7.4
何も出ない

privateメソッドはオーバーライドされることはないよ、というE_WARNINGが出ます。
警告なので動作自体はするのですが、今後動かなくなる可能性もありますし、使わない方がよいでしょう。

If an object constructor exit()s, the object destructor will no longer be called

コンストラクタでexit()した場合、デストラクタが呼ばれなくなりました。

class FOO
{
    public function __construct()
    {
        exit();
    }

    public function __destruct()
    {
        echo 'destruct called.';
    }
}
new FOO();

// PHP8.0
何も出ない
// PHP7.4
destruct called.

元々コンストラクタでthrowしたときはデストラクタが呼ばれていなかったのですが、それと動作を合わせたということらしいです。

Non-strict comparisons between numbers and non-numeric strings now work by casting the number to string and comparing the strings

厳密でない比較演算子の数値比較の挙動が変更になりました。
詳しくはここに書いてあります

var_dump('foo' == 0);

// PHP8.0
false
// PHP7.4
true

厳密な比較演算子の挙動は変更ありません。
===を使っている限りは安泰です。

Namespaced names can no longer contain whitespace

名前空間に空白を入れることができなくなりました。

namespace A \ B{}

// PHP8.0
Parse error:  syntax error, unexpected token "\"
// PHP7.4
エラー出ない

まあ、今までできていたのがおかしいといえばおかしいのですが。

Nested ternaries now require explicit parentheses

曖昧な三項演算子のネストに括弧が必要になりました。

1 ? 2 : 3 ?: 4;

// PHP8.0
Fatal error:  Unparenthesized `a ? b : c ?: d` is not supported
// PHP7.4
Deprecated:  Unparenthesized `a ? b : c ?: d` is deprecated

PHPの三項演算子は他の大半の言語と挙動が異なるという問題がありましたが、それを合わせるための布石です。

debug_backtrace() and Exception::getTrace() will no longer provide references to arguments

debug_backtraceおよびException::getTraceが引数の参照を渡さなくなりました。

function foo(&$bar)
{
    debug_backtrace()[0]['args'][0] = 2;
}

$x = 1;
foo($x);
echo $x;

// PHP8.0
1
// PHP7.4
2

これまでスタックトレース経由で引数を書き替えるという邪悪な操作ができていたのですが、今後はできなくなります。

Numeric string handling has been altered to be more intuitive and less error-prone

数値型文字列の定義が変更になりました

function foo(int $int){}
foo('123abc');

// PHP8.0
Fatal error:  Uncaught TypeError: foo(): Argument #1 ($int) must be of type int
// PHP7.4
Notice:  A non well formed numeric value encountered

PHP7.4までは数値型文字列の定義が『数値型文字列』と『非正規の数値型文字列』の2種類あってわかりにくかったのですが、今後は単純に『数値型文字列』とそれ以外になります。

Magic Methods will now have their arguments and return types checked if they have them declared

マジックメソッドがシグネチャをチェックするようになりました。

class FOO{
    public function __get(array $name):int{
        return 1;
    }
}

// PHP8.0
PHP Fatal error:  FOO::__get(): Parameter #1 ($name) must be of type string when declared
// PHP7.4
エラー出ない

正しい定義に合わせるか、あるいは単にシグネチャを書かないようにすればよいです。

// PHP8.0でもOK
class FOO{
    public function __get($name){
        return 1;
    }
}

call_user_func_array() array keys will now be interpreted as parameter names, instead of being silently ignored

call_user_func_arrayに連想配列を渡したときに、キーが無視されなくなりました。

function foo(...$args){
    var_dump($args);
}
call_user_func_array('foo', ['a'=>1, 'b'=>2]);

// PHP8.0
['a'=>1, 'b'=>2]
// PHP7.4
[1, 2]

そもそもcall_user_func_arrayなんか使うなって話だな。

COM

Removed the ability to import case-insensitive constants from type libraries

COMのタイプライブラリ定数の大文字小文字を区別しない機能が削除されました。
com_load_typelibの第二引数は常にtrueとなり、com.autoregister_casesensitiveも常にonとなります。

ということらしいのだけど、そもそもタイプライブラリって何だ。

Curl

CURLOPT_POSTFIELDS no longer accepts objects as arrays

CURLOPT_POSTFIELDSはオブジェクトを受け取らなくなりました。

$c = curl_init();
curl_setopt($c, CURLOPT_POSTFIELDS, (object)['a'=>1]);

// PHP8.0
Fatal error:  Uncaught Error: Object of class stdClass could not be converted to string
// PHP7.4
エラー出ない

そもそも最初から文字列か配列しか受け取らないという仕様で、オブジェクトを受け取れていたのはたまたまです。
今後は普通に配列を渡せば問題ありません。

Date

mktime() and gmmktime() now require at least one argument

mktimegmmktimeは、最低ひとつの引数が必要になりました。

mktime();

// PHP8.0
Fatal error:  Uncaught ArgumentCountError: mktime() expects at least 1 argument, 0 given
// PHP7.4
Deprecated:  mktime(): You should be using the time()

単に現在時刻がほしいときはtimeを使え、ということらしいです。

DOM

Remove unimplemented classes from ext/dom that had no behavior and contained test data

定義だけあって実装のなかった、あるいは実験的実装だったDOM関連クラスが削除されました。


new DOMTypeInfo();

// PHP8.0
Fatal error:  Uncaught Error: Class "DOMTypeInfo" not found
// PHP7.4
エラー出ない

具体的にはDOMNameList / DomImplementationList / DOMConfiguration / DomError / DomErrorHandler / DOMImplementationSource / DOMLocator / DOMUserDataHandler / DOMTypeInfoが削除されました。
これらはDOMスタンダードからも削除されているため、それと合わせたものとなります。
マニュアルにも元々入ってなかったので影響も極小でしょう。

Enchant

enchant_broker_list_dicts(), enchant_broker_describe() and enchant_dict_suggest() will now return an empty array instead of null

enchant_broker_list_dicts等の関数が、対象がなにもないときにnullではなく[]を返すようになりました。

enchant_broker_init() will now return an EnchantBroker object rather than a resource

enchant_broker_initは、これまでリソースを返していたのがEnchantBrokerオブジェクトを返すようになりました。

他のEnchant関数はこれまでと同じ書き方で透過的に動作します。

enchant_broker_request_dict() and enchant_broker_request_pwl_dict() will now return an EnchantDictionary object rather than a resource

enchant_broker_request_dictenchant_broker_request_pwl_dictは、これまでリソースを返していたのがEnchantDictionaryオブジェクトを返すようになりました。

ていうかEnchantってPHP7.3で削除されるという話だったんだけどどうなったんだ?

Exif

Removed read_exif_data

read_exif_dataが削除されました。

今後はexif_read_dataを使いましょう。

Filter

The FILTER_FLAG_SCHEME_REQUIRED and FILTER_FLAG_HOST_REQUIRED flags for the FILTER_VALIDATE_URL filter have been removed

検証フィルタFILTER_VALIDATE_URLにおいて、FILTER_FLAG_SCHEME_REQUIREDFILTER_FLAG_HOST_REQUIREDのフラグが削除されました。

実はプロトコルスキームとホストは元から常に必須だったため、実質的に意味のないフラグでした。

The INPUT_REQUEST and INPUT_SESSION source for filter_input() etc have been removed

filter_inputなどのフラグINPUT_REQUESTとINPUT_SESSIONが削除されました。

そもそも実装されていませんでした。

GD

The GD extension now uses a GdImage objects as the underlying data structure for images, rather than resources

GDリソースがGdImageオブジェクトになりました。

$image = imagecreatetruecolor(100, 100);
$text_color = imagecolorallocate($image, 233, 14, 91);
imagestring($image, 1, 5, 5, 'TEXT', $text_color);
imagepng($image);

echo gettype($image);

// PHP8.0
object
// PHP7.4
resource

この変更は透過的です。
上記コードの$imageはPHP7.4ではリソースであるのに対してPHP8.0ではオブジェクトになりますが、どちらでも同じように動作します。

ただしis_resourceなどでチェックを入れている場合は動かなくなります。

The deprecated function image2wbmp() has been removed

image2wbmp関数が削除されました。

PHP7.3以降E_DEPRECATEでした。

The deprecated functions png2wbmp() and jpeg2wbmp() have been removed

同じく非推奨だったpng2wbmpjpeg2wbmpも削除されました。

The default $mode parameter of imagecropauto() no longer accepts -1.

imagecropautoの第二引数$mode-1を受け付けなくなりました。

$image = imagecreatetruecolor(100, 100);
imagecropauto($image, -1, 0.5);

// PHP8.0
Fatal error:  Uncaught ValueError: imagecropauto(): Argument #2 ($mode) must be a valid mode
// PHP7.4
Deprecated:  imagecropauto(): Crop mode -1 is deprecated

IMG_CROP_DEFAULTなどの定数を使いましょう。

GMP

gmp_random() has been removed

gmp_randomが削除されました。

この関数はプラットフォームに依存して出力が変わるという問題があったため、PHP7.2でE_DEPRECATEDになりました。
かわりにgmp_random_bitsgmp_random_rangeを使いましょう。

Iconv

iconv() implementations which do not properly set errno in case of errors are no longer supported

iconvは、エラー時にerrnoプロパティを適切にセットしない実装はサポートしなくなりました。

と書かれているのですが、errnoがなんなのか一切出てこないので何を見て判断すればよいのかわかりません。

IMAP

The unused default_host argument of imap_headerinfo() has been removed

imap_headerinfoの未使用だった第五引数$defaulthostが削除されました。

The imap_header() function which is an alias of imap_headerinfo() has been removed

imap_headerが削除されました。

imap_headerinfoのエイリアスです。

Intl

The deprecated constant INTL_IDNA_VARIANT_2003 has been removed

定数INTL_IDNA_VARIANT_2003が削除されました。

これはUnicodeから削除されたためです。

The deprecated Normalizer::NONE constant has been removed

Normalizer::NONEが削除されました。

何もしないという意味のない設定です。

The IntlDateFormatter::RELATIVE_FULL, RELATIVE_LONG, RELATIVE_MEDIUM, and RELATIVE_SHORT constants have been added.

IntlDateFormatterクラスに定数RELATIVE_FULL/RELATIVE_LONG/RELATIVE_MEDIUM/RELATIVE_SHORTが追加されました。

追加は互換性のない変更ではないような気がする。

LDAP

The deprecated function ldap_sort / ldap_control_paged_result / ldap_control_paged_result_response has been removed

関数ldap_sortldap_control_paged_resultldap_control_paged_result_responseが削除されました。

かわりにldap_searchを使いましょう。

The interface of ldap_set_rebind_proc has changed

ldap_set_rebind_procの第二引数$callbackが空白""を受け付けなくなりました。

設定を外したい場合はかわりにnullを使います。

Mbstring

The mbstring.func_overload directive has been removed

マルチバイト関数の関数オーバーロード機能が削除されました。

substrを使うと自動的にmb_substrが呼ばれる、といった機能なのですが、事故の元でしかありません。

機能の削除に伴い、定数MB_OVERLOAD_MAILmb_get_infofunc_overloadといった関連項目も削除されます。

mb_parse_str() can no longer be used without specifying a result array

mb_parse_strの第二引数$arrayが必須になりました。

mb_parse_str('a=b');
echo $a;

// PHP8.0
Fatal error:  Uncaught ArgumentCountError: mb_parse_str() expects exactly 2 arguments, 1 given
// PHP7.4
b // Deprecated:  mb_parse_str(): Calling mb_parse_str() without the result argument is deprecated

元々は文字列を直接変数に展開するという危険極まりない関数だったのですが、この変更により配列に展開するという妥当な内容になりました。
$array = mb_parse_str('a=b')とできればもっと良かったのですけどね。

A number of deprecated mbregex aliases have been removed

いくつかのマルチバイト正規表現関数に存在したエイリアスが削除されました。

mbsplit("//u", 'foo');

// PHP8.0
Fatal error:  Uncaught Error: Call to undefined function mbsplit
// PHP7.4
Deprecated:  Function mbsplit() is deprecated

なぜかmb_eregmb_regex_encodingなどには、mberegmbregex_encodingといった_のないエイリアスが存在しました。
これらはマニュアルにも全く存在しない、謎のエイリアスです。

The 'e' modifier for mb_ereg_replace() has been removed

mb_ereg_replaceにおいてオプションeが削除されました。

preg_replaceでは7.0で削除されたのに何故かこっちには残っていたのですが、ようやく揃った形です。

今後はmb_ereg_replace_callbackを使う必要があります。

A non-string pattern argument to mb_ereg_replace() will now be interpreted as a string instead of an ASCII codepoint

mb_ereg_replaceにおいて、第一引数$patternのASCIIコード変換を行わなくなります。

echo mb_ereg_replace(98, 'x', 'abc98');

// PHP8.0
"abcx"
// PHP7.4
"axc98" // Deprecated:  mb_ereg_replace(): Non-string patterns will be interpreted as strings in the future

第一引数に数値を渡した場合、これまでは何故か対応するASCIIコードとして扱われていました。
意味もないうえにわかりにくすぎるので、今後は単純に文字列として扱われます。

The needle argument for mb_strpos(), mb_strrpos(), mb_stripos(), mb_strripos(), mb_strstr(), mb_stristr(), mb_strrchr() and mb_strrichr() can now be empty

mb_strposなどの各関数において、引数$needleに空白が許可されます。

echo mb_strpos('abcde', '');

// PHP8.0
0
// PHP7.4
false // Warning:  mb_strpos(): Empty delimiter

""はあらゆる文字列にマッチするため、だいたい1文字目になります。

The $is_hex parameter, which was not used internally, has been removed from mb_decode_numericentity()

mb_decode_numericentityにおいて、使われていなかった引数$is_hexが削除されました。

The legacy behavior of passing the encoding as the third argument instead of an offset for the mb_strrpos() function has been removed

mb_strrposの、引数$encodingは第四引数ですが、これを第三引数に書いても動く機能が削除されました。

mb_strrpos('b', 'abc', 'UTF-8');

// PHP8.0
Fatal error:  Uncaught TypeError: mb_strrpos(): Argument #3 ($offset) must be of type int, string given
// PHP7.4
Deprecated:  mb_strrpos(): Passing the encoding as third parameter is deprecated

引数の位置が変更されたのはPHP5.2です。
今まで残ってたことにびっくりですね。

The ISO_8859-* character encoding aliases have been replaced by ISO8859-* aliases

サポートされる文字エンコーディングのうち、ISO-8859-xのエイリアスが変更されました。

mb_detect_order(['ISO-8859-1', 'ISO_8859-2']);
var_dump(mb_detect_order());

// PHP8.0
Fatal error:  Uncaught ValueError: mb_detect_order(): Argument #1 ($encoding) contains invalid encoding
// PHP7.4
['ISO-8859-1', 'ISO-8859-2']

これまでISO_8859-xという形のエイリアスが存在したのですが、PHP8.0では使えなくなりました。
かわりにISO8859-xというエイリアスが使えるようになりますが、これはPHP7.4では対応していません。

iconvと形を揃えるためだそうですが、いきなり完全に差し替える必要に陥ります。
とはいえ、最初からエイリアスではなくISO-8859-xの正しい形を使っていれば特に問題ありません。

mb_ereg() and mb_eregi() will now return boolean true on a successful match

mb_eregmb_eregiが、マッチしたらtrueを返すようになりました。

echo mb_ereg('bbb', 'abbbc', $regs);

// PHP8.0
true
// PHP7.4
3

これまでは第三引数$regsの有無によって返り値の値も型も変わるという非常に使いにくいものでした。
この変更によって、マッチしたらtrue、しなかったらfalseとわかりやすい形になります。

OCI8

The OCI-Lob class is now called OCILob, and the OCI-Collection class is now called OCICollection

OCI-LobクラスがOCILobに、OCI-CollectionクラスがOCICollectionに変更されました。

Several alias functions have been marked as deprecated

幾つかの関数のエイリアスが非推奨になりました。

oci_internal_debug() and its alias ociinternaldebug() have been removed

oci_internal_debugが削除されました。

ODBC

odbc_connect() no longer reuses persistent connections

odbc_connectが持続的接続を利用しなくなりました。

よくわかりませんが、odbc_pconnectのほうに同じ dsn、user、 password の組み合わせ (odbc_connect() および odbc_pconnect() による)接続の場合は、 持続的な接続を再利用するとか書いてあるので、おそらく一度odbc_pconnectしたあとにodbc_connectしたら持続的接続になってしまっていたのではないかと思われます。

The unused flags parameter of odbc_exec() has been removed

odbc_execの第三引数$flagsは未使用だったため削除されました。

OpenSSL

openssl_x509_read() and openssl_csr_sign() will now return an OpenSSLCertificate object rather than a resource

openssl_x509_readopenssl_csr_signは、これまでリソースを返していたのをOpenSSLCertificateオブジェクトを返すようになりました。

The openssl_x509_free() function is deprecated

openssl_x509_freeはE_DEPRECATEDとなり、何もしなくなりました。

↑の変更で対象がリソースからオブジェクトになったので、わざわざ関数で開放しなくても自動的に解放されるからです。

openssl_csr_new() will now return an OpenSSLCertificateSigningRequest object rather than a resource

openssl_csr_newもリソースではなくOpenSSLCertificateSigningRequestオブジェクトを返すようになりました。

openssl_pkey_new() will now return an OpenSSLAsymmetricKey object rather than a resource

openssl_pkey_newもOpenSSLAsymmetricKeyオブジェクトを以下略

The openssl_pkey_free() function is deprecated

openssl_pkey_freeはE_DEPRECATEDとなり、何もしなくなりました。

openssl_seal() and openssl_open() now require $method to be passed

openssl_sealopenssl_openの引数$methodが必須になりました。
デフォルト値が'RC4'で、これはセキュアではないためです。

PCRE

When passing invalid escape sequences they are no longer interpreted as literals

無効なエスケープシーエンスが文字列リテラルと解釈されなくなりました。

preg_match('/\i/', 'h\ij', $matches);
var_dump($matches);

// PHP8.0
null // Warning:  preg_match(): Compilation failed: unrecognized character
// PHP7.4
[0=>"i"]

正規表現パターンに存在しない文字列\iは、これまでは単なる文字列と解釈されていましたが、今後は不正なパターンとみなされてエラーが出るようになります。
\iという文字列で検索したい場合は、正しく\\\\iと記述しましょう。

PDO

The default error handling mode has been changed from "silent" to "exceptions"

PDOエラーモードPDO::ATTR_ERRMODEのデフォルト値がPDO::ERRMODE_EXCEPTIONになりました。
これまではPDO::ERRMODE_SILENTでした。

The signatures of some PDO methods have changed

幾つかのメソッドにおいてシグネチャが変更されました。
例としてPDOStatement::setFetchModeは、PDOStatement::setFetchMode(int $mode, ...$params)になります。

PDO_ODBC

The php.ini directive pdo_odbc.db2_instance_name has been removed

pdo_odbc.db2_instance_nameディレクティブが削除されました。

これを設定すると環境変数DB2INSTANCEに値が入るだけというよくわからないディレクティブなので、まあ削除されても妥当でしょう。

pgsql

The deprecated pg_connect() syntax using multiple parameters instead of a connection string is no longer supported

pg_connectにおいて、DSNを使わない形の古い構文が削除されました。

pg_connect("host", "port", "options", "tty", "dbname");

// PHP8.0
Fatal error:  Uncaught ArgumentCountError: pg_connect() expects at most 2 arguments, 5 given
// PHP7.4
エラー出ない

mysqliもPDOも既にDSN形式以外は対応していないので、こちらもいつまでも残しておく必要はないでしょう。

The deprecated pg_lo_import() and pg_lo_export() signature that passes the connection as the last argument is no longer supported

pg_lo_importおよびpg_lo_exportは、実は第三引数にリソースを渡しても動いていたらしいのですが、それが削除されました。

そんな構文ドキュメントにもユーザノートにも全く書かれていないのですが、PHP7.4のソースにはたしかにそれっぽいものが書いてあって、PHP8.0ではそれがなくなっていました。
これ使ってる人とかいるんだろうか。

pg_fetch_all() will now return an empty array instead of false for result sets with zero rows

pg_fetch_allは、取得行数が0だった場合はfalseを返していたのですが、[]を返すようになりました。

失敗ではなく結果が無いということなので、こちらが妥当でしょう。

Phar

Metadata associated with a phar will no longer be automatically unserialized

これまではPharファイルを読み込もうとした時点で自動的にメタデータが展開されていたのですが、自動的には呼ばれなくなりました。

セキュリティ上の理由です。

Reflection

The method signatures have been changed

幾つかの関数のシグネチャが変更されました。

// PHP7.4
ReflectionClass::newInstance($args)
ReflectionFunction::invoke($args)
ReflectionMethod::invoke($object, $args)

// PHP8.0
ReflectionClass::newInstance(...$args)
ReflectionFunction::invoke(...$args)
ReflectionMethod::invoke($object, ...$args)

The ReflectionType::__toString() method will now return a complete debug representation of the type, and is no longer deprecated

ReflectionType::__toStringは非推奨でなくなりました。

function hoge(?int $a){}
echo (new ReflectionFunction('hoge'))->getParameters()[0]->getType();

// PHP8.0
?int
// PHP7.4
int

また、これまで正確に取ってこれなかったnullableなども取ってこれるようになっています。

Reflection export() methods have been removed

Reflection::exportは削除されました。

ReflectionFunction::export('strpos');

// PHP8.0
Fatal error:  Uncaught Error: Call to undefined method ReflectionFunction::export()
// PHP7.4
Function [ <internal:standard> function strpos ]

ざっくり概要確認するには便利だったんですけどね。

The following methods can now return information about default values of parameters of internal functions

ReflectionParameter::isDefaultValueAvailablegetDefaultValueisDefaultValueConstantgetDefaultValueConstantNameが内部関数にも使えるようになりました。

(new ReflectionFunction('strpos'))->getParameters()[2]->getDefaultValue();

// PHP8.0
0
// PHP7.4
Fatal error:  Uncaught ReflectionException: Cannot determine default value for internal functions

内部関数に対して使う意義はあんまり感じられませんが。

ReflectionMethod::isConstructor() and ReflectionMethod::isDestructor() now also return true

ReflectionMethod::isConstructorおよびisDestructorは、インターフェイスにもtrueを返すようになりました。
これまではインターフェイスには非対応でした。

interface HOGE{
	public function __construct();
}
(new ReflectionFunction('strpos'))->getParameters()[2]->getDefaultValue();

// PHP8.0
true
// PHP7.4
false

ちなみにtraitには昔からtrueを返します。

ReflectionType::isBuiltin() method has been moved to ReflectionNamedType

ReflectionType::isBuiltinReflectionNamedTypeクラスに移動しました。
なぜならReflectionUnionTypeにはビルトインの型はないからです。

Sockets

The deprecated AI_IDN_ALLOW_UNASSIGNED and AI_IDN_USE_STD3_ASCII_RULES flags for socket_addrinfo_lookup() have been removed

socket_addrinfo_lookup関数の第三引数に渡せるフラグAI_IDN_ALLOW_UNASSIGNED/AI_IDN_USE_STD3_ASCII_RULESが削除されました。

glibcで削除されたので追随したということのようです。

socket_create(), socket_create_listen(), etc will now return a Socket object rather than a resource

以下の各関数が、リソースではなくオブジェクトを返すようになりました。
socket_create / socket_create_listen / socket_accept / socket_import_stream / socket_addrinfo_connect / socket_addrinfo_bind / socket_wsaprotocol_info_import

$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_set_option($socket, IPPROTO_IP, IP_MULTICAST_LOOP, 0);
var_dump($socket);

// PHP8.0
object(Socket)#1 (0) {}
// PHP7.4
resource of type (Socket)

socket_set_optionのようなソケットを引数として受け取る関数は、そのまま透過的に動作します。
ただしis_resource等でチェックしている場合は修正が必要です。

socket_addrinfo_lookup() will now return an array of AddressInfo objects rather than resources

socket_addrinfo_lookupは、リソースの配列ではなく、AddressInfoの配列を返すようになりました。

SPL

SplFileObject::fgetss() has been removed

SplFileObject::fgetssは削除されました。

SplHeap::compare($a, $b) now specifies a method signature

SplHeap::compareに型mixedが設定されました。
extendsする場合は型を指定するか、もしくは何も書かないかが必要です。

SplDoublyLinkedList::push() now returns void instead of true

SplDoublyLinkedList::pushがvoidを返すようになりました。

var_dump((new SplDoublyLinkedList())->push(1));

// PHP8.0
null
// PHP7.4
true

これまではtrueを返していました。
元々マニュアルにもvoidと書かれていて、trueを返すのはバグだったみたいです。

SplDoublyLinkedList::unshift() now returns void instead of true

SplDoublyLinkedList::unshiftがvoidを以下同文。

SplQueue::enqueue() now returns void instead of true

SplQueue::enqueueが以下同文。

spl_autoload_register() will now always throw a TypeError on invalid arguments

spl_autoload_registerは、無効な引数について常にTypeErrorを発するようになりました。

spl_autoload_register('HOGE', false);

// PHP8.0
Fatal error:  Uncaught TypeError: spl_autoload_register(): Argument #1 ($callback) must be a valid callback, function "HOGE" not found or invalid function name
// PHP7.4
何も出ない

これに伴い、第二引数$throwは常に無視されるようになります。

SplFixedArray is now an IteratorAggregate and not an Iterator

SplFixedArrayIteratorAggregateをimplementsしました。

var_dump(
	is_subclass_of('SplFixedArray', 'Iterator'),
	is_subclass_of('SplFixedArray', 'IteratorAggregate'),
);

// PHP8.0
false, true
// PHP7.4
true, false

これまではIteratorをimplementsしていました。
また、これに伴ってSplFixedArray::rewindなどIterator由来の実装は削除され、かわりにSplFixedArray::getIteratorが追加されます。

Standard

assert() will no longer evaluate string arguments

assertの引数を文字列表現で渡すことができなくなりました。

assert('$a');

// PHP8.0
true
// PHP7.4
Notice:  Undefined variable: a

PHP7.4までは、引数が文字列のときはPHPコードとして解釈されていました。
つまりevalされていたってことです。

PHP8では'$a'という単なる文字列として扱われます。

parse_str() can no longer be used without specifying a result array

parse_strの第二引数$resultが必須になりました。
上の方にあるmb_parse_strと全く同じ理由です。

fgetss() has been removed

fgetssは削除されました。

The string.strip_tags filter has been removed

文字列フィルタstring.strip_tagsが削除されました。
fgetssなどと同様、何かをするのと同時に文字列をフィルタするのは事故の元なのでやめましょうという方向です。

The needle argument of strpos(), strrpos(), stripos(), strripos(), strstr(), strchr(), strrchr(), and stristr() will now always be interpreted as a string

strposほかの関数で、引数$needleに空文字列が有効になりました。

echo strpos('abc', '');

// PHP8.0
0
// PHP7.4
Warning:  strpos(): Empty needle

空文字はどのような文字列にも該当するので、だいたいは1文字目すなわち0になります。

The length argument for substr(), substr_count(), substr_compare(), and iconv_substr() can now be null

substrsubstr_countsubstr_compareiconv_substrの引数$lengthにnullを渡した時の挙動が変わりました。

echo substr('abcde', 2, null);

// PHP8.0
"cde"
// PHP7.4
""

これまでは返り値が0やら""やらになっていたのですが、これが未指定の場合と同じ動きになります。

The length argument for array_splice() can now be null

array_spliceの引数$lengthにnullを渡した時の挙動が変わりました。

$input = [1, 2, 3, 4];
array_splice($input, 2, null);
var_dump($input);

// PHP8.0
[1, 2]
// PHP7.4
[1, 2, 3, 4]

これまでは何もしませんでしたが、未指定の場合と同じ動きになります。
すなわち、引数$offsetより後ろが全て削除されます。

The args argument of vsprintf(), vfprintf(), and vprintf() must now be an array

vsprintfvfprintfvprintfの引数$argが配列しか受け付けなくなりました。

echo vsprintf('a%sc', 'b');

// PHP8.0
Fatal error:  Uncaught TypeError: vsprintf(): Argument #2 ($values) must be of type array, string given
// PHP7.4
abc

元からマニュアルでは配列のみとなっていたのに、何故か文字列なども受け付けていたという謎のサービスでした。

The 'salt' option of password_hash() is no longer supported

password_hashのオプションsaltが削除されました。
指定する意味はないし固定すると脆弱になるだけなので、元々存在する必要のないオプションです。

The quotemeta() function will now return an empty string if an empty string was passed

quotemetaにから文字列""を渡すと空文字列""を返すようになりました。
これまではfalseでした。

hebrevc() / convert_cyr_string() / money_format() / ezmlm_hash() / restore_include_path() has been removed

以下の関数が削除されました。
hebrevc
convert_cyr_string
money_format
ezmlm_hash
restore_include_path

上位互換が存在する、他の関数と整合性がないなどの理由で、PHP7.4で非推奨となっています。

get_magic_quotes_gpc() and get_magic_quotes_runtime() has been removed

旧時代PHPの黒歴史のひとつ、get_magic_quotes_gpcget_magic_quotes_runtimeが削除されました。
同時に定数FILTER_SANITIZE_MAGIC_QUOTESも削除されました。

PHPはついに、マジッククオートの軛から完全に解き放たれたのです。

Calling implode() with parameters in a reverse order ($pieces, $glue) is no longer supported

implodeは歴史的理由から引数を逆順でも受け付けていたのですが、これが削除されました。

echo implode(['a', 'b', 'c'], '|');

// PHP8.0
Fatal error:  Uncaught TypeError: implode(): Argument #2 ($array) must be of type ?array, string given
// PHP7.4
a|b|c

PHP8.0は、負の遺産の清算が目立ちます。

parse_url() will now distinguish absent and empty queries and fragments

parse_urlの、空のクエリやフラグメントへの返り値が微妙に変わりました。

// 出力のscheme等は省略
var_dump(
    parse_url('http://example.com/foo'),
    parse_url('http://example.com/foo?'),
    parse_url('http://example.com/foo#'),
    parse_url('http://example.com/foo?#'),
);

// PHP8.0
[], ["query"=>""], ["fragment"=>""], ["query"=>"", "fragment"=>""]
// PHP7.4
[], [], [], []

空のクエリには空文字列を出力、クエリ自体がない場合は出力自体行わない、と区別できるようになります。

var_dump() and debug_zval_dump() will now print floating-point numbers using serialize_precision rather than precision

var_dumpdebug_zval_dumpの浮動小数出力が、ini設定precisionではなくserialize_precisionを使うようになりました。

ini_set('precision', 1);ini_set('serialize_precision', 100);
var_dump(1.23456789012345678901234567890123456789012345678901234567890);

// PHP8.0
float(1.2345678901234566904321354741114191710948944091796875)
// PHP7.4
float(1)

100とか指定してもビット数の制限は越えられないみたい。

If the array returned by __sleep() contains non-existing properties, these are now silently ignored

マジックメソッド__sleepに存在しないプロパティを返した場合、無視するようになりました。

class FOO{
    private $a = 'a';
    public function __sleep()
    {
        return ['a', 'b'];
    }
}
echo serialize(new FOO());

// PHP8.0
O:3:"FOO":1:{s:6:"FOOa";s:1:"a";} // Warning:  serialize(): "b" returned as member variable from __sleep() but does not exist
// PHP7.4
O:3:"FOO":2:{s:6:"FOOa";s:1:"a";s:1:"b";N;} // Notice:  serialize(): "b" returned as member variable from __sleep() but does not exist

これまでは、値がnullのプロパティがセットされていました。

The default locale on startup is now always "C"

デフォルトのロケールが常に"C"になりました。
これまではLC_ALLは"C"で、LC_CTYPEは環境変数LANGから取ってくる、みたいなことになっていました。

Removed deprecated DES fallback in crypt()

cryptのSALTフォールバックが削除されました。

echo crypt('str', 'あ');

// PHP8.0
*0
// PHP7.4
▒UYLTFzPe08. // Deprecated:  crypt(): Supplied salt is not valid for DES

cryptはSALTの形式によって自動的にハッシュ形式を選択するのですが、これまでは未知のSALTが来た場合は自動的にDESにフォールバックしていました。
今後は失敗します。
ただ失敗といっても例外を出したりせず'*0'という文字列を返してくるのでむしろわかりにくいのでは。

Calling crypt() without an explicit salt is no longer supported

cryptの第二引数$saltが必須になりました。

echo crypt('str');

// PHP8.0
Fatal error:  Uncaught ArgumentCountError: crypt() expects exactly 2 arguments, 1 given
// PHP7.4
$1$SsnnMRiS$4clL.zBKekzZvryhRYVUP1 // Notice:  crypt(): No salt parameter was specified

こちらは非対応SALTとは異なり、致命的エラーになります。

Sort comparison functions that return true or false will now throw a deprecation warning

ソート関数でtrue/falseを返している場合、非推奨の警告が出るようになりました。
0と等しいか、0より大きいか、0より小さい整数を返す必要があります。

$arr = [2, 1];
usort($arr, fn($a, $b) => $a > $b);
var_dump($arr);

// PHP8.0
[1, 2] // Deprecated:  usort(): Returning bool from comparison function is deprecated
// PHP7.4
[1, 2]

Any functions accepting callbacks that are not explicitly specified to accept parameters by reference will now warn

リファレンスを受け取らないコールバック関数の引数にリファレンスを書いた場合、警告が出るようになりました。

array_filter([1,2], function (&$v) {
    return true;
});

// PHP8.0
Warning:  {closure}(): Argument #1 ($v) must be passed by reference, value given
// PHP7.4
何も出ない

多くの関数では既にこうなっていたものの、array_filterなど一部はそうなってなかったので追加したということのようです。
それに$vを書き替えても元の値には影響しないので、&を付ける意味は特にないです。

HTTP stream wrapper as used by functions now advertises HTTP/1.1 rather than HTTP/1.0

stream_context_createなどで作れるHTTPストリームラッパーのデフォルトがHTTP1.1になりました。

これまでどおりHTTP1.0を使いたい場合は、コンテキストオプションで指定する必要があります。

$opts = [
    'http'=>[
        'protocol_version'=>'1.0',
    ]
];
$context = stream_context_create($opts);

substr(), mb_substr(), iconv_substr() and grapheme_substr() now consistently clamp out-of-bounds offsets to the string boundary

substrmb_substriconv_substrgrapheme_substrが、範囲外の引数にも文字列を返すようになりました。

var_dump(
    substr('abc', 10)
);

// PHP8.0
""
// PHP7.4
false

これまではfalseになっていました。

Populating $http_response_header variable by the HTTP stream wrapper doesn't force rebuilding of symbol table anymore

HTTPストリームラッパーが$http_response_headerを変更してもシンボルテーブルの再構築が行われなくなりました。

ということらしいけど内部のことはよくわかりません。
きっと詳しい人がなんか教えてくれるはず。

Sysvmsg

msg_get_queue() will now return an SysvMessageQueue object rather than a resource

msg_get_queueがリソースではなくSysvMessageQueueインスタンスを返すようになりました。

Sysvsem

sem_get() will now return an SysvSemaphore object rather than a resource

sem_getがリソースではなくSysvSemaphoreインスタンスを返すようになりました。

The $auto_release parameter of sem_get() was changed to accept bool values rather than int

sem_getの第4引数$auto_releaseの型がintからboolになりました。

Sysvshm

shm_attach() will now return an SysvSharedMemory object rather than a resource

shm_attachがリソースではなくSysvSharedMemoryインスタンスを返すようになりました。

tidy

The $use_include_path parameter has been removed from tidy_repair_string()

tidy_repair_stringの引数$use_include_pathが削除されました。

元々マニュアルにも載ってなかったんだけど、内部的に使っていたらしいです。

tidy::repairString() and tidy::repairFile() became static methods

tidy::repairStringtidy::repairFileが静的メソッドになりました。

tidy::repairString('hoge');

// PHP8.0
エラー出ない
// PHP7.4
Fatal error:  Uncaught Error: Non-static method tidy::repairString() cannot be called statically

Tokenizer

T_COMMENT tokens will no longer include a trailing newline

T_COMMENTトークンは末尾の改行を含まなくなりました。

$str = '<?php // comment
';
var_dump(
    token_get_all($str)[1][1]
);

// PHP8.0
string(10) "// comment"
// PHP7.4
string(11) "// comment
"

Namespaced names are now represented

名前空間はひとまとまりとして解釈されるようになりました。

// 出力は必要部分のみ
$str = '<?php
namespace FOO\BAR;
';
var_dump(
    token_get_all($str)
);

// PHP8.0
[ [T_NAME_RELATIVE, 'FOO\BAR'] ]
// PHP7.4
[ [T_STRING, 'FOO'], [T_NS_SEPARATOR, '\'], [T_STRING, 'BAR'] ]

こちらのほうが名前空間としてわかりやすいですね。

XML

xml_parser_create(_ns) will now return an XmlParser object rather than a resource

xml_parser_createがリソースではなくXmlParserオブジェクトを返すようになりました。

$parser = xml_parser_create();
xml_parse($parser, '<xml></xml>', true);
var_dump($parser);

// PHP8.0
object(XmlParser)#1
// PHP7.4
resource(4) of type (xml)

XMLパーサ関数は、これまでと同じ書き方で透過的に動作します。
またxml_parser_freeは実質的に何もしません。

XMLReader

XMLReader::open() and XMLReader::xml() are now static methods

XMLReader::openXMLReader::XMLは静的メソッドになりました。

XMLWriter

The XMLWriter functions now accept and return, respectively, XMLWriter objects instead of resources

XMLWriter::openUriなどがリソースではなくXMLWriterオブジェクトを返すようになりました。
XMLWriter関数は、これまでと同じ書き方で透過的に動作します。

Zip

ZipArchive::OPSYS_Z_CPM has been removed

定数ZipArchive::OPSYS_Z_CPMが削除されました。

echo ZipArchive::OPSYS_Z_CPM;

// PHP8.0
Fatal error:  Uncaught Error: Undefined constant ZipArchive::OPSYS_Z_CPM
// PHP7.4
9

これ、ZipArchive::OPSYS_CPMの誤字です。

Zlib

gzgetss() has been removed

gzgetssが削除されました。

inflate_init() will now return an InflateContext object rather than a resource

inflate_initがリソースではなくInflateContextオブジェクトを返すようになりました。
inflate_addなどは、これまでと同じ書き方で透過的に動作します。

deflate_init() will now return a DeflateContext object rather than a resource

deflate_initがリソースではなくDeflateContextオブジェクトを返すようになりました。
deflate_addなどは、これまでと同じ書き方で透過的に動作します。

zlib.output_compression is no longer automatically disabled for Content-Type: image/*

ini設定zlib.output_compressionは、これまでは画像に対しては自動的に無効化されていましたが、その自動無効化がなくなりました。

New Features

新機能。

この多くはPHP8.0の新機能で紹介しています。

Core

Added support for union types

UNION型が追加されました。

function hoge(int|string $fuga): bool|stdClass {
    if($fuga === 0){return true;}
    return new stdClass;
}

関数hogeはint型もしくはstring型を受け取り、bool型もしくはstdClassインスタンスを返します。

Added WeakMap

弱いマップです。
メモリが足りなくなったら自動で削除されます。

Added ValueError class

ValueErrorクラスが追加されました。

echo bcadd('1', '2', -1);

// PHP8.0
Fatal error:  Uncaught ValueError: bcadd(): Argument #3 ($scale) must be between 0 and 2147483647
// PHP7.4
3

bcaddの第三引数は小数点以下の桁数なので、マイナス値は不適切です。
このように、型は合っているけど値が範囲外のときに使われる例外となります。

Any number of function parameters may now be replaced by a variadic argument

継承時に、異なる型の値を可変長引数でまとめることができるようになりました。

class A{
    public function hoge(int $foo, string $bar, stdClass $baz){}
}
class B extends A{
    public function hoge(...$args){}
}

// PHP8.0
エラー出ない
// PHP7.4
Warning:  Declaration of B::hoge(...$args) should be compatible with A::hoge

この場合、シグネチャは継承したクラスのものしか確認しません。
すなわち、(new B())->hoge(1, 2, 3)とか書いてもエラーは出ません。

これはいいのか??????

"static" (as in "late static binding") can now be used as a return type

返り値にstaticを書けるようになります。

class Test {
    public function create(): static {
        return new static();
    }
}

使い道がよくわからないので誰か解説よろ。

It is now possible to fetch the class name of an object using $object::class

インスタンスからクラス名を取得できるようになります。

echo (new stdClass)::class;

// PHP8.0
stdClass
// PHP7.4
Fatal error:  Cannot use ::class with dynamic class name

New and instanceof can now be used with arbitrary expressions

Some consistency fixes to variable syntax have been applied

デリファレンス可能範囲が拡大されます。

class Foo{
    const BAR = 'Bar';
}
class Bar{
    public static $baz = 'QUX';
}
echo Foo::BAR::$baz;

// PHP8.0
QUX
// PHP7.4
Parse error:  syntax error, unexpected '::'

これまでは構文エラーになっていたいくつかの構文が許されるようになります。
このRFCややこしくて正直理解が追い付いてません。

Added Stringable interface

Stringableインターフェイスが追加されました。

interface Stringable{
   public function __toString(): string;
}

これがあると__toString()可能になるというものですが、これは暗黙的にimplementsされるため、使用する側としては特に気にする必要はありません。

Traits can now define abstract private methods

traitでabstract privateメソッドが作成可能になりました。

trait FOO {
    abstract private function bar();
}

// PHP8.0
エラー出ない
// PHP7.4
Fatal error:  Abstract function FOO::bar() cannot be declared private

throw can now be used as an expression

throwが式になりました。

fn() => throw new Exception();

// PHP8.0
エラー出ない
// PHP7.4
Parse error:  syntax error, unexpected 'throw'

構文の途中などで気軽に例外を投げられるようになります。

An optional trailing comma is now allowed in parameter lists

関数定義に末尾カンマが許可されました。

function foo($a, $b,){}

// PHP8.0
エラー出ない
// PHP7.4
Parse error:  syntax error, unexpected ')'

関数を呼び出す方はPHP7.3から使えていたので、合わせた対応です。

It is now possible to write catch (Exception) to catch an exception without storing

例外の変数を明示的に受け取らないことができるようになりました。

try{
    new PDO();
}catch(\Throwable){
    echo "なんかエラー出た";
}

// PHP8.0
なんかエラー出た
// PHP7.4
Parse error:  syntax error, unexpected ')'

PDOのエラーメッセージを表示すると脆弱性になる可能性があるので出したくない場合など、例外から来た値を使わずに処理したいという意図を明示できます。

Added support for mixed type

mixed型がサポートされました。

function foo(mixed $arg){}
foo(1);
foo('x');
foo(new stdClass());

// PHP8.0
エラー出ない
// PHP7.4
Uncaught TypeError: Argument 1 passed to foo() must be an instance of mixed, int given

マニュアルでは見慣れた型名ですね。
var_dumpの引数など、あえて多様な型を受け取りたいときに使います。

Added support for Attributes

アトリビュートが追加されました。

#[ExampleAttribute]
function foo(){}

Javaでいうアノテーションです。
これまではPHPDocとかで適当に書いていましたが、これがPHPの構文としてパース可能な形で書けるようになります。

Added support for constructor property promotion

オブジェクト初期化子です。
素直にコンストラクタ引数昇格って言った方がわかりやすい気がしてきた。

class HOGE{
    public function __construct(public int $x){}
}
echo (new HOGE(99))->x;

// PHP8.0
99
// PHP7.4
Parse error:  syntax error, unexpected 'public'

コンストラクタ引数に可視性を書くと、自動的に同名のプロパティを作って値を突っ込んでくれます。
定型文をたくさん書く必要があって面倒だった初期化が楽になります。

コンストラクタ以外には使えません。

Added support for match expression

match式が導入されました。

echo match("1"){
    1 => '1だ',
    true => 'trueだ',
    "1" => '"1"だ',
    default => 'どれでもない',
};

// PHP8.0
"1"// PHP7.4
Parse error:  syntax error, unexpected '=>'

厳密な比較、フォールスルーしない、返り値を持つ式である、とswitch文の問題の多くを解消した構文です。

Private methods declared on a parent class no longer enforce any inheritance rules

privateメソッドが子クラスに影響しないようになりました。

class A{ 
    private final function foo(int $arg){}
}
class B extends A{ 
    private function foo(string $agg){}
}

// PHP8.0
Warning:  Private methods cannot be final
// PHP7.4
Fatal error:  Cannot override final method A::foo() 

privateメソッドにfinalって書くと継承時に何故かFatal Errorになっていたのですが、これがオーバーライドチェックされないようになります。

かわりにprivateは上書きされないからfinalしなくていいというE_WARNINGが出るようになりました。

Added support for nullsafe operator

ヌル安全オペレータが追加されました。

echo null?->foo();

// PHP8.0
NULL
// PHP7.4
Parse error:  syntax error, unexpected '->'

ヌル安全オペレータ?->は、nullに対してプロパティやメソッドを取り出そうとすると、そこで処理が止まります。

なおnullでなければ止まらないので、(new stdClass)?->foo()とか書くとfoo()が無いってエラーになります。

Added support for named arguments

名前付き引数がサポートされました。

echo htmlspecialchars(encoding:'UTF-8', string:'>');

// PHP8.0
&gt;
// PHP7.4
Parse error:  syntax error, unexpected ':'

引数の多い関数で最後の引数だけ呼んだり、引数の順番を入れ替えたりできます。
また呼び出し側に引数名を書くため、どの引数が何を表しているのか理解しやすくなります。

Date

Added DateTime::createFromInterface() and DateTimeImmutable::createFromInterface()

DateTime::createFromInterfaceDateTimeImmutable::createFromInterfaceが追加されました。

echo (DateTime::createFromInterface(new DateTimeImmutable()))->format('Y-m-d');

// PHP8.0
2020-12-01
// PHP7.4
Fatal error:  Uncaught Error: Call to undefined method DateTime::createFromInterface

名前からするとDateTime::createFromImmutableのように、DateTimeXXXからDateTimeを作るメソッドだと思われます。
現状ではDateTimeとDateTimeImmutableしかないうえにDateTimeInterfaceをユーザがimplementsすることはできないので意味はありませんが、将来種類が増えたときに役立ちそうです。

Added the DateTime format specifier "p"

DateTimeのフォーマットpが追加されました。
Pと同じですが、+00:00のときだけZが返ります。よくわからない。

Dom

Introduce DOMParentNode and DOMChildNode

DOMParentNode、DOMChildNodeが追加されました。
普段SimpleXMLしか使わないからよくわからない。

Enchant

enchant_dict_add / enchant_dict_is_added

enchant_dict_addenchant_dict_is_addedが追加されました。

Enchantを実用で使ってる人っているんだろうか。

FPM

Added a new option pm.status_listen

設定pm.status_listenが追加されました。
異なるエンドポイントのステータスを確認できるとかなんとか。

Hash

HashContext objects can now be serialized

HashContextがシリアライズ可能になりました。

echo serialize(hash_init('md5'));

// PHP8.0
O:11:"HashContext":5// PHP7.4
Fatal error:  Uncaught Exception: Serialization of 'HashContext' is not allowed

リクエストを超えてハッシュ作成を継続したいとかあるのか?

Opcache

If the opcache.record_warnings ini setting is enabled, opcache will record compile-time warnings

ini設定opcache.record_warningsを有効にした場合、コンパイル時に発生した警告をして次のアクセス時にも発生させます。

OpenSSL

Added Cryptographic Message Syntax (CMS)

Cryptographic Message Syntaxがサポートされました。

Standard

printf() and friends now support the %h and %H format specifiers

printfおよび類似関数が、指定子%h%Hをサポートしました。
%g%Gと同じですが、ロケールに関わらず小数点が.になります。

setlocale(LC_ALL, 'fr');
echo sprintf('%g - %h', 1.2, 3.4);

// PHP8.0
1,2 -
// PHP7.4
1,2 - 3.4

上の方にもあったフランス語問題の対応です。

printf() and friends now support using "*" as width or precision

printfおよび類似関数が、量指定子*をサポートしました。

printf("%.*f", 10, 1.2);

// PHP8.0
1.2000000000
// PHP7.4
f

精度を外側から渡すことができます。
これまでも文字列結合などで実現は可能でしたが、簡単に書けるようになります。

proc_open() now supports pseudo-terminal (PTY) descriptors

proc_openが疑似ターミナルをサポートしました。

$proc = proc_open('dir', [['pty'], ['pty'], ['pty']], $pipes);

// PHP8.0
Warning:  proc_open(): PTY (pseudoterminal) not supported on this system
// PHP7.4
Warning:  proc_open(): pty pseudo terminal not supported on this system

なんにしろWindowsではサポートされてなかった。

proc_open() now supports socket pair descriptors

proc_openがソケットをサポートしました。

$proc = proc_open('dir', [['socket'], ['socket'], ['socket']], $pipes);

// PHP8.0
エラー出ない
// PHP7.4
Warning:  proc_open(): socket is not a valid descriptor spec/mode

こちらはWindowsでも使えます。

Sorting functions are now stable

ソート関数が安定ソートになりました。

$array = [
    'a' => 1,
    'b' => 1,
];
asort($array);

これまではソート後にaとbの位置がどうなっているか保証がなかったのですが、今後はaとbの順番が保たれます。

array_diff(), array_intersect() and their variations can now be used with a single array as argument

array_diffなどを引数ひとつで使えるようになりました。

array_diff([1, 2]);

// PHP8.0
エラー出ない
// PHP7.4
Warning:  array_diff(): at least 2 parameters are required, 1 given

単独だと意味はないですが、array_intersect(...$arrays)のように展開して渡したいときに役立ちます。

The $flag parameter of ob_implicit_flush() was changed to accept bool values

ob_implicit_flushの引数$flagがbool型になりました。

Zip

Extension updated to version 1.19.1

zipエクステンションのバージョンが1.19.1になりました。
と言われてもこれがlibzipのことかすらよくわからないのですが。

New ZipArchive::lastId property

ZipArchiveにlastIdプロパティが追加されました。
最後に追加したファイルのインデックスです。

Error can be checked after an archive is closed

ZipArchiveを閉じた後でも、ZipArchive::statusなどでステータスを確認可能になりました。

The remove_path option of ZipArchive::addGlob() and ::addPattern() is now treated as arbitrary string prefix

ZipArchive::addGlobおよびZipArchive::addPatternのオプションremove_pathは、アーカイブを追加する際に削除される接頭辞になりました。
以前はディレクトリ名でしたが、add_pathと整合が取れていなかったので変更されました。

Optional compression / encryption features are listed in phpinfo

圧縮/暗号化オプションがphpinfoに表示されるようになりました。

Changes in SAPI modules

SAPIモジュールへの変更

Apache

The PHP module has been renamed

Apache PHPモジュールの名前がphp7_moduleからphp_moduleに変更されました。

今後メジャーバージョンアップのたびにLoadModule php8_moduleとか書き替えずに済むようになります。

Deprecated Functionality

非推奨になる機能。
ここで『できなくなった』と書いてあるのは非推奨と読み替えてください。

Core

Declaring a required parameter after an optional one is deprecated

省略可能引数の後で必須引数を記述することができなくなりました。

function hoge($foo = 'foo', $bar){}

// PHP8.0
Deprecated:  Required parameter $bar follows optional parameter $foo
// PHP7.4
エラー出ない

今まで許可されていたことにびっくりですね。

Calling get_defined_functions() with $exclude_disabled explicitly set to false is deprecated

get_defined_functionsの引数$exclude_disabledにfalse指定ができなくなりました。

互換性のない変更点において、無効化された関数は存在しないものにされました。
そのため、このフラグには意味がなくなりました。

Enchant

enchant_broker_set_dict_path and enchant_broker_get_dict_path not available

libenchant-2では、enchant_broker_set_dict_pathenchant_broker_get_dict_pathが使えなくなりました。

enchant_dict_add_to_personal, use enchant_dict_add instead

enchant_dict_add_to_personalが非推奨になりました。
かわりにenchant_dict_addを使います。

enchant_dict_is_in_session, use enchant_dict_is_added instead

enchant_dict_is_in_sessionが非推奨になりました。
かわりにenchant_dict_is_addedを使います。

enchant_broker_free and enchant_broker_free_dict, unset the object instead

enchant_broker_freeenchant_broker_free_dictが非推奨になりました。
かわりにunsetを使います。

ENCHANT_MYSPELL and ENCHANT_ISPELL constants

定数が非推奨になりました。

LibXML

libxml_disable_entity_loader() has been deprecated

libxml_disable_entity_loaderが非推奨になりました。
XXE脆弱性対応のためです。

PGSQL / PDO PGSQL

The constant PG_VERSION_STR has now the same value as PG_VERSION, and thus is deprecated

定数PG_VERSION_STRは中身がPG_VERSIONと同じになり、さらに非推奨になりました。

PG_VERSION_STRもPG_VERSIONもPostgresの項目ににないんだけど何処で定義されてるんだろう。

Function aliases in the pgsql extension have been deprecated

Postgres関数の古い呼び名のエイリアスが非推奨になりました。

たとえばpg_lo_openにはpg_loopenというエイリアスが存在し、PHP7.4までは警告なく使用可能でした。

Zip

Using empty file as ZipArchive is deprecated

空のファイルをZipArchiveとして扱うことができなくなりました。
libzip 1.6.0で非対応になったためです。

The procedural API of Zip is deprecated

手続き型のzip関数は非推奨になりました。

zip_open('tmp');

// PHP8.0
Deprecated:  Function zip_open() is deprecated
// PHP7.4
エラー出ない

ZipArchiveを使いましょう。

Reflection

ReflectionFunction::isDisabled() is deprecated

ReflectionFunction::isDisabledが非推奨になりました。
無効化された関数は存在しないものにされたためです。

ReflectionParameter::getClass(), ReflectionParameter::isArray(), and ReflectionParameter::isCallable() are deprecated

ReflectionParameter::getClassReflectionParameter::isArrayReflectionParameter::isCallableが非推奨になりました。

かわりにReflectionParameter::getTypeReflectionTypeを使いましょう。

Changed Functions

仕様が変更になる関数。

Reflection

ReflectionClass::getConstants and ReflectionClass::getReflectionConstants results can be now filtered

https://www.php.net/manual/ja/reflectionclass.getconstantsReflectionClass::getReflectionConstantsに引数$filterが追加されました。
またフィルタ用の定数ReflectionClassConstant::[IS_PUBLIC|IS_PROTECTED|IS_PRIVATE]が追加されました。

class FOO{
    public const A = 'A';
    protected const B = 'B';
    private const C = 'C';
}
$c = (new ReflectionClass('FOO'))->getReflectionConstants(filter:ReflectionClassConstant::IS_PRIVATE);
// privateの定数Cだけ出てくる

引数未指定のときはこれまでどおり全て出てきます。

Zip

ZipArchive::addGlob and ZipArchive::addPattern methods accept more values in the "options"

ZipArchive::addGlobZipArchive::addPatternの引数$optionsに対象が追加されました。

  • flags
  • comp_method
  • comp_flags
  • env_method
  • enc_password

ZipArchive::addEmptyDir, ZipArchive::addFile and aZipArchive::addFromString methods have a new "flags" argument

ZipArchive::addEmptyDirZipArchive::addFileZipArchive::addFromStringに引数$flagsが追加されました。

ZipArchive::extractTo now restore file modification time

ZipArchive::extractToがmtimeを復元するようになりました。
これまでは解凍した時刻になっていました。

New Functions

新しい関数。

Core

Added get_resource_id($resource) function

関数get_resource_idが追加されました。

echo get_resource_id(tmpfile()); // 4 環境によって変わる

リソースのリソースIDを取得できます。
(int)$resourceと同じですが、関数的に記述することができます。

ldap_count_references

関数ldap_count_referencesが追加されました。

よくわかりませんが、おそらくWindowsAPIなどと同じものでしょう。
他のLDAP関数ほぼ共通ですし。

OpenSSL

Added openssl_cms_encrypt() / openssl_cms_decrypt() / openssl_cms_read() / openssl_cms_sign() / openssl_cms_verify()

CMS関連の関数が追加されました。
Cryptographic Message Syntaxサポートに伴い導入されたものです。

openssl_cms_encrypt('input.txt', 'output.txt', file_get_contents('key.pem'), []);

使い方はopenssl_pkcs7_XXXXXと相似です。

PCRE

Added preg_last_error_msg

preg_last_error_msgが追加されました。

preg_match('/(?:\D+|<\d+>)*[!?]/', 'foobar foobar foobar');
echo preg_last_error_msg(); // Backtrack limit exhausted

正規表現で最後に発生したエラーメッセージを返します。
意味的にはpreg_last_errorと同じですが、こちらはintでわかりにくいため、メッセージとして出力するようにしたものです。

SQLite3

Add SQLite3::setAuthorizer() and respective class constants

SQLite3::setAuthorizerが追加されました。

SQLを実行するたびに呼ばれるコールバックを設定することができます。

Standard

str_contains / str_starts_with / str_ends_with

str_containsstr_starts_withstr_ends_withが追加されました。

str_contains('森久保乃々', '森久保'); // true
str_starts_with('望月聖', '望月'); // true
str_ends_with('イヴ・サンタクロース', 'サンタクロース'); // true

それぞれ○○が含まれる○○で始まる/終わる、の文字列検索を楽にする関数です。

Added fdiv() function

fdivが追加されました。

echo fdiv(1, 0); // INF

IEEE 754に準拠した割り算です。

Added get_debug_type() function

get_debug_typeが追加されました。

echo get_debug_type(1), get_debug_type(new DateTime()); // int, DateTime

プリミティブ型の型名を返すgettypeと、オブジェクトのクラス名を返すget_class悪魔合体です。

Zip

ZipArchive::setMtimeName and ZipArchive::setMtimeIndex

ZipArchive::setMtimeNameZipArchive::setMtimeIndexが追加されました。
mtimeを操作できます。

ZipArchive::setProgressCallback / ZipArchive::setCancelCallback

ZipArchive::setProgressCallbackとZipArchive::setCancelCallbackが追加されました。

ってUPGRADINGには書いてあるんだけど、試してみるとCall to undefinedって言われるしregisterProgressCallbackregisterCancelCallbackが存在するのでおそらく間違い。

ZipArchive::replaceFile

ZipArchive::replaceFileが追加されました。
中身のファイルの一部を置き換えるみたいだけど、そんなややこしいことをPHPで行うことはあるのだろうか。

ZipArchive::isCompressionMethodSupported / ZipArchive::isEncryptionMethodSupported

ZipArchive::isCompressionMethodSupportedZipArchive::isEncryptionMethodSupportedが追加されました。

名前のとおり、圧縮方式および暗号化方式をサポートしているか確認できます。

New Classes and Interfaces

新しいクラス、インターフェイス。

Tokenizer

PhpToken

PhpTokenクラスが追加されました。

echo PhpToken::tokenize('<?php echo; ?>')[0]->text; // <?php

既存のTokenizerをオブジェクト的に扱えるようにしたものです。

Removed Extensions and SAPIs

削除されたエクステンション、SAPIなど。

XML-RPC

The xmlrpc extension has been unbundled and moved to PECL

XML-RPCがPHPコアから削除され、PECL行きになりました。
内部で使っているxmlrpc-epi開発放棄されているためです。

Other Changes to Extensions

そのほかの変更点。

CURL

The CURL extension now requires at least libcurl 7.29.0

Curlエクステンションがlibcurlの7.29.0以上を必要とするようになりました。

curl_init() will now return a CurlHandle object rather than a resource

curl_initはリソースではなくCurlHandleオブジェクトを返すようになりました。

var_dump(
    curl_init()
);

// PHP8.0
object(CurlHandle)#1
// PHP7.4
resource(4) of type (curl)

他のCurl関数はこれまでと同じ書き方で透過的に動作します。
curl_closeは実質的に何もしなくなりました。

curl_multi_init() will now return a CurlMultiHandle object rather than a resource

curl_multi_initはリソースではなくCurlMultiHandleオブジェクトを返すようになりました。

それ以外はcurl_initとほぼ同じです。

curl_share_init() will now return a CurlShareHandle object rather than a resource

curl_share_initはリソースではなくCurlShareHandleオブジェクトを返すようになりました。

それ以外はcurl_initとほぼ同じです。

The deprecated parameter $version of curl_version() has been removed

非推奨だったcurl_versionの第一引数$versionが削除されました。

マニュアルからは既に抹消されています。

Date

DatePeriod now implements IteratorAggregate

DatePeriodIteratorAggregateをimplementsするようになりました。

(new ReflectionClass('DatePeriod'))->getInterfaceNames(); // [IteratorAggregate, Traversable]

以前はTraversableでした。

DOM

DOMNamedNodeMap / DOMNodeList now implements IteratorAggregate

DOMNamedNodeMapおよびDOMNodeListIteratorAggregateをimplementsするようになりました。
これらも以前はTraversableでした。

Intl

IntlBreakIterator / ResourceBundle now implements IteratorAggregate

IntlBreakIteratorおよびResourceBundleIteratorAggregateをimplementsするようになりました。
以前はTraversableでした。

Enchant

The enchant extension now uses libenchant-2 by default when available

Enchantエクステンションが使用しているlibenchantのバージョンがlibenchant-2になりました。
1系は非推奨となり、将来的に削除されます。

GD

The $num_points parameter of imagepolygon(), imageopenpolygon() and imagefilledpolygon() is now optional

imagepolygonimageopenpolygonimagefilledpolygonの引数$num_pointsが省略可能になりました。
省略した場合、$points / 2とみなされます。

imagepolygon( $im=imagecreatetruecolor(100, 100), [1,2,3,4,5,6], imagecolorallocate($im, 255,255,255))

第四引数$colorは省略不可のため、省略する場合は第三引数に$colorを入れる、すなわち引数の数によって第三引数の意味が変わるという非常にわかりにくいことになります。
負債を増やしてどうする。

imagepolygon( $im=imagecreatetruecolor(100, 100), [1,2,3,4,5,6], null, imagecolorallocate($im, 255,255,255));
// Fatal error:  Uncaught ValueError: imagepolygon(): Argument #3 ($num_points_or_color) must be greater than or equal to 3

imagepolygon( $im=imagecreatetruecolor(100, 100), points:[1,2,3,4,5,6], color:imagecolorallocate($im, 255,255,255))
// Fatal error:  Uncaught ArgumentCountError: imagepolygon(): Argument #3 ($num_points_or_color) not passed

nullにしたり名前付き引数で飛ばしたりといったこともできません。
これはもっとどうにかしたほうがよかったのでは。

The function imagegetinterpolation() to get the current interpolation method has been added

imagegetinterpolationが追加されました。

$im=imagecreatetruecolor(100, 100);
imagesetinterpolation($im, IMG_GENERALIZED_CUBIC);
echo imagegetinterpolation($im); // 11

現在設定されている補間方法を取得します。

JSON

The JSON extension cannot be disabled anymore

JSONエクステンションを無効にすることができなくなりました。

マニュアルには全く載っていないのですが、実はこれまでコンパイルオプション--disable-jsonで無効化することが可能でした。

MBString

The Unicode data tables have been updated to version 13.0.0

Unicodeデータベースが13.0.0にバージョンアップされました。

PDO

PDOStatement now implements IteratorAggregate

PDOStatementIteratorAggregateをimplementsするようになりました。
以前はTraversableでした。

LibXML

The minimum required libxml version is now 2.9.0

要求するlibxmlがバージョン2.9.0以上になりました。

以前は2.6.0以上でした。

MySQLi / PDO MySQL

When mysqlnd is not used the minimum supported libmysqlclient version is now 5.5

要求するlibmysqlclientのバージョンが5.5以上になりました。
libmysqlclientではなくmysqlndを使う場合は不要です。

ところでマニュアルには『mysqlndを勧める』って書いてあるんだけど、変更履歴では『libmysqlclientが推奨だしデフォルトもこっち』って書いてあるんですよね。
どっちが正解なんだ。

mysqli_result now implements IteratorAggregate

mysqli_resultIteratorAggregateをimplementsするようになりました。
以前はTraversableでした。

PGSQL / PDO PGSQL

The PGSQL and PDO PGSQL extensions now require at least libpq 9.1

要求するlibpqのバージョンが9.1以上になりました。

Readline

Calling readline_completion_function() before the interactive prompt starts will now override the default interactive prompt completion function

readline_completion_functionは、対話型シェルが起動していないときにも動くようになりました。
以前は、既に対話型シェルが起動していないと動きませんでした。

SimpleXML

SimpleXMLElement now implements RecursiveIterator and absorbed the functionality of SimpleXMLIterator

SimpleXMLElementRecursiveIteratorをimplementsするようになりました。
以前はTraversableでした。

さらにSimpleXMLIteratorの機能を吸収しました。
SimpleXMLIteratorは実質的にSimpleXMLElementと同じになりました。

Shmop

shmop_open() will now return a Shmop object rather than a resource

shmop_openはリソースではなくShmopオブジェクトを返すようになりました。

他の共有メモリ関数はこれまでと同じ書き方で透過的に動作します。
shmop_closeは実質的に何もしなくなります。

New Global Constants

新たなグローバル定数。

Filter

FILTER_VALIDATE_BOOL has been added as an alias for FILTER_VALIDATE_BOOLEAN

検証フィルタFILTER_VALIDATE_BOOLが追加されました。

var_dump(FILTER_VALIDATE_BOOL, FILTER_VALIDATE_BOOLEAN); // 258, 258

これはFILTER_VALIDATE_BOOLEANのエイリアスです。
真偽型の表記はboolに揃えられつつあるので、他のbooleanもいずれboolに変更されるでしょう。

Changes to INI File Handling

iniファイルの変更。

zend.exception_string_param_max_len

ini設定zend.exception_string_param_max_len追加されました
スタックトレースに表示される引数の文字数を制御します。

try {
    substr('12345678901234567890');
} catch(Throwable $e) {
    echo $e->getTraceAsString();
}

// zend.exception_string_param_max_len=15のとき
#0 C:\xampp\htdocs\test.php(10): substr('123456789012345...')

// zend.exception_string_param_max_len=1のとき
#0 C:\xampp\htdocs\test.php(6): substr('1...')

開発時のデバッグなどに重宝するでしょう。

デフォルトは15で、範囲は0から1000000です。
ini_setで変更できたのでPHP_INI_ALLのようです。

com.dotnet_version

dotnetを使う際の.netフレームワークのバージョンを指定できるようです。

Windows Support

Standard

Program execution functions using the shell now consistently execute %comspec% /s /c "$commandline",

execなどのシェルコマンド実行関数が、一貫して%comspec% /s /c "$commandline"という形で実行されるようになりました。

ということらしいですがよくわかりません。

GD

php_gd2.dll has been renamed to php_gd.dll

GDエクステンションのファイル名がphp_gd2.dllからphp_gd.dllに変更されました。

php-test-pack

The test runner has been renamed from run-test.php to run-tests.php

テストパッケージのテストランナーファイル名がrun-test.phpからrun-tests.phpに変更されました。

Other Changes

その他の変更点

EBCDIC targets are no longer supported

文字コードEBCDICがサポート対象外になりました。

果たして使っていた人はいるのだろうか?

Performance Improvements

パフォーマンスについて。

A Just-In-Time (JIT) compiler has been added to the opcache extension

JITがサポートされました。

function getFibonacci(int $i){
    return $i < 2 ? $i : getFibonacci($i-1) + getFibonacci($i-2);
}

$a = microtime(true);
echo getFibonacci(38);
$b = microtime(true);
echo $b - $a;

| PHP8デフォルト | opcache有効 | JIT有効 |
|---|---|---|---|
| 6.931639 | 5.833915 | 1.698887 |
| 7.443145 | 5.536612 | 2.009317 |
| 6.610769 | 5.619338 | 1.662739 |
| 6.794568 | 5.654112 | 1.739347 |
| 7.286194 | 5.774223 | 1.788053 |

CPUバウンドな処理はとんでもない速さになります。

ただPHPの主な用途はディスクやデータベースへのIOで、それらにはJITの恩恵はほぼないので、実際のWebアプリケーションはここまで極端に速くはなりません。
公式LPではざっくり1割から3割減というところですね。

感想

変更点多すぎ!!!!
PHP8たいしたことないんじゃねえのとか言ってた奴土下座な。

互換性のない変更の多くは、古くからのアバウトな仕様を排除し、厳密な書き方を推奨する方向に向けるものです。
implodeの引数が逆順でも書けるなんてのはその最たるものですね。
そのぶん昔からの熟成されたソースを使っているところは移行が大変になると思いますが、PHPは互換を切ってでも進化し続ける方向を選んだということなので、まあがんばってもらいましょう。
それにPHP7以降の近代風な書き方をしている限りでは、変更が必要なところはほとんどありません。

そして新機能については、流行りの書式やら文法などを貪欲に取り入れていて、このあたりはさすがPHPってところです。
節操がないとも言えますが、その節操のなさこそがPHPたる所以と言えましょう。
アロー関数や型宣言、アトリビュートあたりはまだ機能が半端なので、次のメジャーバージョンかあるいはマイナーバージョンでさらに進化しそうですね。

さらにJITが入ったことで、Webアプリケーションだけに囚われない、実用的な計算ライブラリでもPHPが輝く時代がやってくるかもしれません。
まあC++やGoあたりとはさすがに比べものにはならないですが、他のスクリプト言語に比べればだいぶいい線行っているのではないでしょうか。

Discussion