Quantcast
Channel: rana_kualuの記事 - Qiita
Viewing all 338 articles
Browse latest View live

【PHP8.0】PHPにオブジェクト初期化子が導入される

$
0
0

これまで何度も塩漬けにされたり却下されたりしていたオブジェクト初期化子ですが、ついにPHP8.0で導入されることになりました。
オブジェクト初期化子が何かというとこれです。

classHOGE{publicfunction__construct(privateint$x){// $HOGE->xが生える}}

これはオブジェクト初期化子でいいのか?

日本語で何と表すのか適切な単語が思いつかなかったのでとりあえずオブジェクト初期化子としておきます。
愚直に訳すと"コンストラクタ引数昇格"ですが、そんな単語は無いうえに型昇格と紛らわしいです。
引数プロパティ宣言パラメータプロパティ宣言もほぼ使われてないし何と表現すればいいのだろう。
きっと誰かが適切な語をプルリクしてくれるはず。

以下は該当のRFC、PHP RFC: Constructor Property Promotionの日本語訳です。

PHP RFC: Constructor Property Promotion

Introduction

PHPでは現在のところ、オブジェクトにプロパティを定義するだけでも同じことを複数回書かなければならないため、多くの無駄が必要です。
以下の単純なクラスを考えてみましょう。

classPoint{publicfloat$x;publicfloat$y;publicfloat$z;publicfunction__construct(float$x=0.0,float$y=0.0,float$z=0.0,){$this->x=$x;$this->y=$y;$this->z=$z;}}

プロパティの表記は、1:プロパティの宣言、2:コンストラクタの引数、3:プロパティの代入で3回も繰り返されます。
さらにプロパティの型も2箇所に書かなければなりません。

プロパティ宣言とコンストラクタ以外には何も含まれていないバリューオブジェクトでは特に、多くの重複によって変更が複雑になり、エラーを起こしやすいものとなります。

このRFCでは、プロパティの定義とコンストラクタを組み合わせるショートハンド構文の導入を提案します。

PHP8
classPoint{publicfunction__construct(publicfloat$x=0.0,publicfloat$y=0.0,publicfloat$z=0.0,){}}

このショートハンド構文は、前述の例と厳密に同じで、より短く書くことができます。
構文は姉妹言語Hackから採用しています。

Proposal

コンストラクタの引数にpublic/protected/private何れかが記述されている場合、その引数は"promoteされた引数"とします。
promoteされた引数には、同じ名前のプロパティが追加され、値が割り当てられます。

Constraints

promoteはabstractではないクラスのコンストラクタでのみ記述可能です。
従って、以下のような構文は使用不能です。

// エラー:コンストラクタではないfunctiontest(private$x){}abstractclassTest{// エラー:abstractなので駄目abstractpublicfunction__construct(private$x);}interfaceTest{// エラー:interfaceも駄目publicfunction__construct(private$x);}

一般的でない使い方ですが、トレイトでは使用可能です。

対応する可視性キーワードはpublic/protected/privateのみです。

classTest{// エラー:varはサポートしてないpublicfunction__construct(var$prop){}}

promoteされた引数によるプロパティは、通常のプロパティと全く同じ扱いになります。
特に注意点として、同じプロパティを二度宣言することはできません。

classTest{public$prop;// Error: Redeclaration of property.publicfunction__construct(public$prop){}}

また、プロパティにすることのできないcallable型は使用することができません。

classTest{// Error: Callable type not supported for properties.publicfunction__construct(publiccallable$callback){}}

promoteされたプロパティはプロパティ宣言と同義であるため、デフォルトがNULLの場合はNULL許容型を明示しなければなりません。

classTest{// Error: Using null default on non-nullable propertypublicfunction__construct(publicType$prop=null){}// こっちはOKpublicfunction__construct(public?Type$prop=null){}}

可変長引数をpromoteすることはできません。

classTest{// エラーpublicfunction__construct(publicstring...$strings){}}

理由としては、明示する引数の型(ここではstring)と、実際に渡される引数の型(ここではstringの配列)が異なるからです。
$stringsプロパティをstringの配列にすることも可能ですが、それではわかりづらくなります。

promoteプロパティと明示的なプロパティ宣言を組み合わせることは可能です。
またpromoteプロパティとpromoteされない引数を同時に渡すことも可能です。

// 正しいclassTest{publicstring$explicitProp;publicfunction__construct(publicint$promotedProp,int$normalArg){$this->explicitProp=(string)$normalArg;}}

Desugaring

promoteプロパティはただのシンタックスシュガーであり、全てのpromoteプロパティに対して以下の変換が適用されます。

// シンタックスシュガーclassTest{publicfunction__construct(publicType$prop=DEFAULT){}}// こう展開されるclassTest{publicType$prop;publicfunction__construct(Type$prop=DEFAULT){$this->prop=$prop;}}

自動的に宣言されるプロパティの可視性と型は、promoteプロパティの可視性および型と同じになります。
注目すべき点は、プロパティにデフォルト値は適用されず(つまり、未初期化で始まります)、コンストラクタ引数でのみ指定されるところです。

プロパティ宣言時にもデフォルト値を指定したほうがよいようにも思えますが、将来的にデフォルト値で指定することが望ましくなるであろう理由が存在します。

ひとつめは、プロパティのデフォルト値に任意の式を利用できるようにする拡張の可能性です。

// FROMclassTest{publicfunction__construct(publicDependency$prop=newDependency()){}}// TOclassTest{publicDependency$prop/* = new Dependency() */;publicfunction__construct(Dependency$prop=newDependency()){$this->prop=$prop;}}

こうなると、プロパティ宣言時とデフォルト値でオブジェクトを2回構築することとなるため望ましくありません。

また、新潟アクセス修正子のルールではプロパティでデフォルト値を宣言すると、コンストラクタで代入することもできなくなります。

promote引数が参照であった場合、プロパティも参照になります。

// FROMclassTest{publicfunction__construct(publicarray&$array){}}// TOclassTest{publicarray$array;publicfunction__construct(array&$array){$this->array=&$array;}}

promoteプロパティへの引数の割り当ては、コンストラクタの冒頭で行われます。
従って、コンストラクタ内でも引数とプロパティの両方にアクセスすることが可能です。

// 動作するclassPositivePoint{publicfunction__construct(publicfloat$x,publicfloat$y){assert($x>=0.0);assert($y>=0.0);}}// こっちも動作するclassPositivePoint{publicfunction__construct(publicfloat$x,publicfloat$y){assert($this->x>=0.0);assert($this->y>=0.0);}}

Reflection

リフレクションおよびその他の解析機構で見ると、シンタックスシュガーを解除した後の状態になります。
すなわち、promoteプロパティは明示的に宣言されたプロパティのように見え、promote引数は通常のコンストラクタ引数のように見える、ということです。

PHPは引数に関するDocコメントを公開していませんが、promoteプロパティのDocコメントも保持されます。

classTest{publicfunction__construct(/** @SomeAnnotation() */public$annotatedProperty){}}$rp=newReflectionProperty(Test::class,'annotatedProperty');echo$rp->getDocComment();// "/** @SomeAnnotation */"

この例のように、promoteプロパティではDocコメントベースのアノテーションを使用することができます。

また、2メソッドが追加されます。

ReflectionProperty::isPromoted()は、promoteプロパティであればtrueを返します。
ReflectionParameter::isPromoted()は、promote引数であればtrueを返します。

プロパティがpromoteされたかどうかを気にする場面はほとんど存在しないと思われますが、この情報によって元のコードをより簡単に再構築することができます。

Inheritance

オブジェクト初期化子は継承することができますが、特に特筆すべきようなことはありません。
abstrautを含む典型的な継承のユースケースを以下に示します。

abstractclassNode{publicfunction__construct(protectedLocation$startLoc=null,protectedLocation$endLoc=null,){}}classParamNodeextendsNode{publicfunction__construct(publicstring$name,publicExprNode$default=null,publicTypeNode$type=null,publicbool$byRef=false,publicbool$variadic=false,Location$startLoc=null,Location$endLoc=null,){parent::__construct($startLoc,$endLoc);}}

ParamNodeクラスでいくつかのpromoteプロパティを宣言し、さらに二つの普通の引数を親コンストラクタに転送しています。
これは以下のように展開されます。

abstractclassNode{protectedLocation$startLoc;protectedLocation$endLoc;publicfunction__construct(Location$startLoc=null,Location$endLoc=null,){$this->startLoc=$startLoc;$this->endLoc=$endLoc;}}classParamNodeextendsNode{publicstring$name;publicExprNode$default;publicTypeNode$type;publicbool$byRef;publicbool$variadic;publicfunction__construct(string$name,ExprNode$default=null,TypeNode$type=null,bool$byRef=false,bool$variadic=false,Location$startLoc=null,Location$endLoc=null,){$this->name=$name;$this->default=$default;$this->type=$type;$this->byRef=$byRef;$this->variadic=$variadic;parent::__construct($startLoc,$endLoc);}}

プロパティへの代入は、親コンストラクタが呼ばれる前に行われることに注意してください。
これはコーディングスタイルとして一般的ではありませんが、動作に影響が出るようなことはほぼありません。

Attributes

PHP8ではアトリビュートも導入されるため、相互作用を考慮する必要があります。
アトリビュートは、プロパティと引数の両方で使用することができます。

classTest{publicfunction__construct(<<ExampleAttribute>>publicint$prop,){}}

このコードがどのように解釈されるか決める必要があります。
1. アトリビュートは引数にのみ適用する。
2. アトリビュートはプロパティにのみ適用する。
3. アトリビュートは引数とプロパティの両方に適用する。
4. 曖昧さを避けるためエラーにする

// Option 1: アトリビュートは引数にのみ適用するclassTest{publicint$prop;publicfunction__construct(<<ExampleAttribute>>int$prop,){}}// Option 2: アトリビュートはプロパティにのみ適用するclassTest{<<ExampleAttribute>>publicint$prop;publicfunction__construct(int$prop,){}}// Option 3: アトリビュートは引数とプロパティの両方に適用するclassTest{<<ExampleAttribute>>publicint$prop;publicfunction__construct(<<ExampleAttribute>>int$prop,){}}// Option 4: 曖昧さを避けるためエラーにする

このRFCでは3番目、つまり引数とプロパティの両方に適用することを提案しています。
これが最も柔軟性の高い方法だからです。

ただし、これは実装に依ると考えています。
PHP8の実装に関わる作業で、アトリビュートをプロパティにのみ配置した方がよいと判明した場合は、そのように変更される場合があります。

Coding Style Consideration

このセクションではコーディングスタイルの推奨について解説します。
規程ではありません。

promoteプロパティを使用する場合、コンストラクタをクラス最初のメソッドとして、明示的なプロパティ宣言の直後に配置することをお勧めします。
これにより、全ての全てのプロパティが先頭にまとめられ、一目でわかるようになります。
静的メソッドを最初に配置することを要求しているコーディング規約は、コンストラクタを最初に配置するよう規約を調整する必要があります。

promoteプロパティに@paramアノテーションを使用している場合、ドキュメントツールは@varアノテーションも含まれているものとして解釈されるべきです。

// 元のコードclassPoint{/**
     * Create a 3D point.
     *
     * @param float $x The X coordinate.
     * @param float $y The Y coordinate.
     * @param float $z The Z coordinate.
     */publicfunction__construct(publicfloat$x=0.0,publicfloat$y=0.0,publicfloat$z=0.0,){}}// こう解釈するclassPoint{/**
     * @var float $x The X coordinate.
     */publicfloat$x;/**
     * @var float $y The Y coordinate.
     */publicfloat$y;/**
     * @var float $z The Z coordinate.
     */publicfloat$z;/**
     * Create a 3D point.
     *
     * @param float $x The X coordinate.
     * @param float $y The Y coordinate.
     * @param float $z The Z coordinate.
     */publicfunction__construct(float$x=0.0,float$y=0.0,float$z=0.0,){$this->x=$x;$this->y=$y;$this->z=$z;}}

最後に、promoteプロパティは、あくまで一般的なケースをカバーするための便利な省略記法であるに過ぎないことに注意してください。
promoteプロパティはいつでも明示的なプロパティに書き換えることができます。
そのため、この変更は下位互換性を壊すことはありません。

Backward Incompatible Changes

下位互換性のない変更はありません。

Future Scope

Larryが、この機能と他の機能を組み合わせることによってオブジェクト初期化を改善する方法について、より深いビジョンを提供しています

Prior Art

この機能、あるいは類似した機能は多くの言語でサポートされています。
Hack
TypeScript
Kotlin

先行するRFCが存在します。
Automatic property initialization プロパティ宣言は必要とする、より弱い形です。
Constructor Argument Promotion このRFCとほとんど同じです。
Code free constructor Kotlinの文法に基づいています。

Vote

投票期間は2020/05/29まで、2/3+1の賛成が必要です。
このRFCは賛成46反対10で受理されました。

感想

プロパティを書くのが格段に楽になりますね。
後から見るときにプロパティが宣言されているのかどうかちょっとわかりにくそうですが、この機能が使えるのはコンストラクタだけなのでそこだけ抑えていれば大丈夫でしょう。
コンストラクタだけではなく任意のメソッドで使えると便利では、と一瞬思ったものの、これを無制限に使えると完全に収拾が付かなくなってしまうので、やはりコンストラクタだけに留めておくのが賢明そうですね。


Qiitaニュースの過去記事が見えない

$
0
0

そもそも今日初めてQiitaニュースの存在を知ったよ。

Qiitaのヘルプページを見てたらQiitaニュースというメールマガジンについて書かれていました。
で、そこからhttps://zine.qiita.com/qiita-news/のアーカイブにリンクが張ってあります。
ここ、1ページ目こそ表示されているものの、最下部のリンクから2ページ目以降に飛ぼうとすると『お探しのページは見つかりませんでした。』になります。

20200604.png

なお、https://zine.qiita.com/category/qiita-news/というURLだと2ページ目以降が見れます。

ここまで書いた後で気付いたのですが、Qiita Zineのトップの右側からQiitaニュースを選ぶと正しいURLになりますね。
すなわち、最初のヘルプページに書かれているURLが間違っていた(しかも半端に動くせいで気付かなかった)というだけの話だった。

これまで見た中で最も汚いソースコード

$
0
0

今回のコードの汚さには正直ちょっと自信あるよ。
下手すりゃIOCCCで優勝も狙えるかもしれないよ。

ということで、私がこれまで見てきた中で最も汚いソースコードを紹介します。

それは、とあるWebサイトのTwitterハッシュタグ埋め込みのコード。
よくあるやつですね。
普通であればタイムライン埋め込み使って一瞬で終わるやつです。

ただ、そこはけっこうお堅めのクライアントで、普通に埋め込むだけではできない要件が追加でありました。
すなわち、ハッシュタグを乗っ取る荒らし投稿などを絶対に表示させたくない、というものです。
具体的にはクライアントからNGワードのリストをもらって、それらの単語が含まれる投稿は表示しないようにします。

だいぶ昔の話なので実際はvarとか使ってたと思うんだけど、ざっくり書くとこんなかんじ。

constngWords=[/* 省略 */];lettweetList=$.get('TwitterAPI');tweetList.forEach(tweet=>{if(tweet.contains(ngWords)){return;}$('#timeline').append(tweet);});

まあ、そこまではそれなりによくある案件だと思うのですよ。

さて問題はここからだ。

該当の処理が、HTMLのscriptタグ内に埋め込みで書かれていた。

はい、別ファイルにするわけでもなく難読化もされずに、NGワードの一覧がHTML内に堂々と配列のまま突っ込んであったのですよ。
お堅いサイトだってのに、ソースを表示したら上の方に、決してまともなサイトに書かれていてはいけない単語の羅列がですね、「ち××」とか「ま××」とか「う××」とか並んでいるわけですよ。
そりゃもう「弊社なにやってんの…」と天を仰ぎますよ。

私がこのことを知ったのは、とっくに納品されてサービスインが終わった後でのことでした。
そもそも私の関係している案件ではなく、たまたま別件のついでに発見しただけなので、事前に気付きようがありませんしね。

だが担当!お前は!何をやってたんだ!!

なおクライアントも気付いてなかったので、とりあえず見なかったことにしました。

今やそのサイトも消滅し、証拠も全て消えたので、まあ時効ということで。

YEStifications:Googleの中の人が語る、通知許可プロンプトに関するCrUXの統計レポート

$
0
0

以下は、Googleの中の人であるRick Viscomi( Twitter / GitHub )による記事、YEStifications: Exploring how users engage with notification prompts in the Chrome UX Reportの日本語訳です。

YEStifications: Exploring how users engage with notification prompts in the Chrome UX Report

📢 example.com wants to show notifications

Notifications APIはユーザを留めるための効果的な方法ですが、実のところ多くのサイトはユーザへの通知を許可してもらえていません。
よくある問題は、ページが読み込まれると即座に、あるいはどのように使用されるかの説明なしに、いきなり許可を求めるプロンプトを表示することです。
では、ユーザは通知許可プロンプトにどのように反応しているでしょうか。
CrUXの出番です。

Chrome UX Report、通称CrUXには、3万を超えるWebサイトに対して、通知許可プロンプトに対するユーザの反応のデータが存在します。
このデータは、パブリックBigQueryリポジトリで公開されています。
ユーザは通知許可プロンプトにどのように反応しているか、この記事で見ていきましょう。

The state of notification permissions

手始めに、このデータセットに含まれているWebサイトの総数を数えてみましょう。

SELECTCOUNT(DISTINCTorigin)FROM`chrome-ux-report.all.202001`WHEREexperimental.permission.notificationsISNOTNULL

結果は31515で、通知のパーミッションを含むoriginが31515件あることを示しています。
originはWebサイトの最初の/までのアドレスのことで、たとえばhttps://www.example.comです。
ひとつのWebサイト上でのユーザの挙動は、全てorigin単位にロールアップされています。

次は試しに、SlackのWebサイトについて通知のパーミッションを見てみましょう。

SELECTSUM(experimental.permission.notifications.accept)ASaccept,SUM(experimental.permission.notifications.dismiss)ASdismiss,SUM(experimental.permission.notifications.deny)ASdeny,SUM(experimental.permission.notifications.ignore)AS`ignore`FROM`chrome-ux-report.all.202001`WHEREorigin='https://app.slack.com'
AcceptDismissDenyIgnore
92.10%5.67%1.96%0.28%

01.png

見てのとおり、Slackアプリに対しての通知の許可承諾率は92%と、非常に優れています。
言い換えると、通知許可プロンプトが表示されたときに、92%のユーザは許可を与えることを選択したということです。
残りユーザのうち約6%は許可か拒否かを選択することなくプロンプトを閉じ、2%は明示的に拒否しました。
そして、プロンプトに何もせず放置したユーザは約0%です。

ところで、Slackに対する通知パーミッションの結果は、Web全体の通知パーミッションの結果を端的に表していると言えるでしょうか?
調査対象を3万サイト全てに拡大してみましょう。

SELECTorigin,SUM(experimental.permission.notifications.accept)ASaccept,SUM(experimental.permission.notifications.dismiss)ASdismiss,SUM(experimental.permission.notifications.deny)ASdeny,SUM(experimental.permission.notifications.ignore)AS`ignore`FROM`chrome-ux-report.all.202001`WHEREexperimental.permission.notificationsISNOTNULLGROUPBYoriginORDERBY`ignore`ASC,acceptDESC

02.png

これは何を表しているでしょうか。
チャート右上のグレー部分は、通知許可プロンプトを無視したユーザの割合を示しています。
次に、チャート中央の最もスペースを取っている赤い領域は、通知許可プロンプトを明示的に拒否した割合です。
そして、チャート左端の急減している緑色の領域が、通知許可プロンプトを許可したユーザの割合です。
最後に、許可と拒否の間にある黄色が、許可も拒否も選択せずに通知許可プロンプトを閉じた割合です。

この図から、いくつかの推論を得られそうです。

最もわかりやすいのは、チャートの結果は赤が大多数であり、緑は少ないということです。
大半のユーザは通知許可プロンプトを拒否しています。
すなわち、Slackは極端に許可率の高い例外中の例外と言えそうです。

また、拒否率と無視率には逆相関がありそうです。
拒否率が小さくなると、無視率が高くなるようです。

Slackが明らかに例外であることはわかりましたが、それでは平均的なWebサイトでの割合はどのようなものでしょうか。

SELECTAVG(accept/total)ASaccept,AVG(dismiss/total)ASdismiss,AVG(deny/total)ASdeny,AVG(`ignore`/total)AS`ignore`FROM(SELECTSUM(experimental.permission.notifications.accept)ASaccept,SUM(experimental.permission.notifications.deny)ASdeny,SUM(experimental.permission.notifications.ignore)AS`ignore`,SUM(experimental.permission.notifications.dismiss)ASdismiss,SUM(experimental.permission.notifications.accept+experimental.permission.notifications.deny+experimental.permission.notifications.ignore+experimental.permission.notifications.dismiss)AStotalFROM`chrome-ux-report.all.202001`WHEREexperimental.permission.notificationsISNOTNULL)
AcceptDismissDenyIgnore
16.71%23.84%40.74%18.71%

03.png

これが現実的な通知許可の割合です。
ただし、ユーザ数が全く異なるWebサイトのUXを平均化したものであることに注意してください。
この結果によると、平均的なWebサイトでは通知許可プロンプトを承認するユーザは17%であり、24%は選択せずに通知許可プロンプトを閉じ、41%が明示的に拒否し、19%が無視するということになりました。

このデータセットから他に何かわかることがないでしょうか?
今度はデバイスのフォームファクタごとに分けてみましょう。

SELECTdevice,AVG(accept/total)ASaccept,AVG(dismiss/total)ASdismiss,AVG(deny/total)ASdeny,AVG(`ignore`/total)AS`ignore`FROM(SELECTform_factor.nameASdevice,SUM(experimental.permission.notifications.accept)ASaccept,SUM(experimental.permission.notifications.deny)ASdeny,SUM(experimental.permission.notifications.ignore)AS`ignore`,SUM(experimental.permission.notifications.dismiss)ASdismiss,SUM(experimental.permission.notifications.accept+experimental.permission.notifications.deny+experimental.permission.notifications.ignore+experimental.permission.notifications.dismiss)AStotalFROM`chrome-ux-report.all.202001`WHEREexperimental.permission.notificationsISNOTNULLGROUPBYdevice)GROUPBYdeviceORDERBYacceptDESC
DeviceAcceptDismissDenyIgnore
スマホ22.76%18.03%57.33%1.88%
タブレット18.75%20.24%58.34%2.67%
PC6.11%34.01%11.61%48.27%

04.png

明白な違いが現れました。
スマホとデスクトップでは、露骨に大きな差があります。
スマホユーザは通知許可プロンプトを無視する割合が非常に低いのに対して、デスクトップユーザは約半数が通知許可プロンプトを無視します。
最も理解しやすい理由は画面サイズでしょう。
通知許可プロンプトが画面の大半を占めている場合、それを無視し続けることは難しいことです。
また興味深いこととしては、スマホユーザは拒否率が57%と非常に高いのに、承認率も23%であり最も高いということです。
理由のひとつとしては、スマホは常に持ち歩くので、通知を受信するには最も自然で便利なフォームファクタであるということがあるかもしれません。

Where we go from here

ユーザは明らかに、押し付けがましい通知許可プロンプトを鬱陶しく感じており、そして開発者もそのことを風の噂に聞いたり実際に経験したりしているかもしれません。
今回我々は、Web全体で通知許可プロンプトがどのように位置付けされているかの実用的なデータを手に入れました。
CrUXのデータから明らかなのは、ユーザに通知許可プロンプトを効果的に承認させることができているサイトが非常に少ないということです。

Chrome開発チームは、ユーザのフラストレーションを減らすために通知許可プロンプトの表示方法変更を発表しました。
通知許可プロンプトの承認率が低いサイトは、より目立たない通知許可プロンプトが自動的に表示されるようになります。
そのため、サイトオーナーは通知許可プロンプトの承認率に注意を払う必要があります。

サイトで通知の許可要求を出しているのであれば、要求に対する反応の割合を調べるためのアクセス解析を追加しましょう。
CrUXデータセットはアクセス解析にはなりませんが、類似したサイトの承認率がどうなっているか調べたり、Web全体のトレンドを調べたりすることに役立ちます。
承認率を向上させ、より多くの効果を得るためには、通知許可プロンプトのベストプラクティスを読むとよいでしょう。

この記事で紹介したクエリは、BigQueryで実行することができます。
クエリを改良して、独自の視点を見つけ出してみるのもよいでしょう。
より詳しい使い方はusing CrUX on BigQueryを参照してください。
データに関する質問やフィードバックがある場合は、以下のいずれかのチャンネルからCrUXチームに連絡をお待ちしています。

chrome-ux-report Google Groups
@ChromeUXReport Twitter
chrome-ux-report StackOverflow
GoogleChrome/CrUX GitHub

コメント欄

「通知を求めるサイトの多さに辟易し、他のみんなは好きなのかと考えていた。Slackのように意味のあるサイト以外ではみんな好きではないようだ。」
「PCでの無視率が大きいのは画面が大きいからなのか、マルチタスクのせいなのか、他に理由があるのか、なんだろう。」
「Skackでの通知は明白なユースケースがある。タブロイド紙とかのユースケースが全く異なるサイトと一緒くたにすると有益な結果が得られないかもしれない。」
「対話的な通知許可プロンプトを出してるサイトとの許可率のちがいを出してほしい。」
「最初にページを表示してから通知許可プロンプトを出すまでの時間でプロットしてみると面白いかも。」「ページロード時に通知許可プロンプトを出しているかはチェックしてるから、今後そのネタで記事を書くかも。」

感想

むしろ2割も承認してることにびっくりだよ。
0.1%くらいかと思ってたよ。

というわけで、何も考えずに通知許可プロンプトを出すと、ほとんどの人が拒否します。
これは想像でも妄想でもなく、歴然とした事実です。
こんなの言うまでもなく考えるでもなく当然なわけですが、そのあたりを何も考えていないな記事やサービスが溢れた結果、当然の帰結としてWebPUSH通知は邪悪なものに成り果てました。
ま、そもそもServiceWorker自体が邪悪な用途にしか使われない技術なので今更ですけどね。

そんなわけで2019年11月、Firefoxはユーザインタラクションなしに通知許可ウィンドウを出すことを禁止しました。
Chromeはこの手の変更には極めて消極的なのでまだ追随していませんが( せいぜい目立たなくした程度 )、いずれは同様の変更が入るでしょう。

この記事の著者はGoogleの中の人なので通知許可プロンプトそのものには否定的ではなく、使うときはうまく導線を考えようという立場です。
全てのWebサイトが適切に通知を実装すれば、それは確かに有益で便利な機能となることでしょう。
しかし、大多数のWebサイトがそのようなガイドラインに従うかというとそんなわけがありません。
従って、ユーザ側として最も適切な設定はデフォルトで一律禁止ということになります。
これだけで、いちいち煩わしい通知許可プロンプトに邪魔されることがなくなり、快適なブラウジングを行うことができるようになります。
Slackなど通知が必要になったらそのときに個別に設定すればいいだけですし、そして通知が必要なサイトはほとんどありません。

【PHP8】厳密なswitch文ことmatch式が使えるようになる

$
0
0

PHPがよく言われる問題点のひとつとして、switch曖昧な比較であるということが挙げられます。

switch($x){case1:'$xは1だよ';break;case"1":'$xは"1"だよ';break;}

case "1"に到達することは決してありません。

ということで厳密な比較を用いるswitchことmatch構文のRFCが提出されました。
以下はMatch expression v2の日本語訳です。

PHP RFC: Match expression v2

Proposal

このRFCは、switchに似ていますが、より安全なセマンティクスを持つmatch構文の提案です。

例として、Doctrineのクエリパーサを挙げます。

// Beforeswitch($this->lexer->lookahead['type']){caseLexer::T_SELECT:$statement=$this->SelectStatement();break;caseLexer::T_UPDATE:$statement=$this->UpdateStatement();break;caseLexer::T_DELETE:$statement=$this->DeleteStatement();break;default:$this->syntaxError('SELECT, UPDATE or DELETE');break;}// After$statement=match($this->lexer->lookahead['type']){Lexer::T_SELECT=>$this->SelectStatement(),Lexer::T_UPDATE=>$this->UpdateStatement(),Lexer::T_DELETE=>$this->DeleteStatement(),default=>$this->syntaxError('SELECT, UPDATE or DELETE'),};

Differences to switch

Return value

後で使いたい値をswitchで生成することは非常によくあることです。

switch(1){case0:$result='Foo';break;case1:$result='Bar';break;case2:$result='Baz';break;}echo$result;//> Bar

そして$resultに代入し忘れることもよくあるミスです。
さらに深くネストされていた場合は、$resultがしっかり代入されているか確認するのもたいへんです。
それに対し、matchは実行した結果が評価される式です。
これによって多くの定型文を削除することができ、代入忘れというミスがなくなります。

echomatch(1){0=>'Foo',1=>'Bar',2=>'Baz',};//> Bar

No type coercion

switch文は緩やかな比較==を使います。
これは直感に反する結果をもたらすことがあります。

switch('foo'){case0:$result="Oh no!\n";break;case'foo':$result="This is what I expected\n";break;}echo$result;//> Oh no!

match式は厳密な比較===で比較します。
strict_typesの設定に関わらず常に厳密です。

echomatch('foo'){0=>"Oh no!\n",'foo'=>"This is what I expected\n",};//> This is what I expected

No fallthrough

switchフォールスルーは、多くの言語でバグの温床となっています。
caseは明示的にbreakしないかぎり、次のcaseへと実行が継続されます。

switch($pressedKey){caseKey::RETURN_:save();// break忘れたcaseKey::DELETE:delete();break;}

match式では、暗黙のbreakを付与することで、この問題を解決します。

match($pressedKey){Key::RETURN_=>save(),Key::DELETE=>delete(),};

複数条件で同じコードを実行したい場合は、条件をカンマで区切ります。

echomatch($x){1,2=>'Same for 1 and 2',3,4=>'Same for 3 and 4',};

Exhaustiveness

switchでよくあるもうひとつの問題は、全てのcaseに対応していない場合の処理です。

switch($operator){caseBinaryOperator::ADD:$result=$lhs+$rhs;break;}// BinaryOperator::SUBTRACTを渡しても何も起こらない

これが原因で、よくわからないところでクラッシュしたり、想定していない動作をしたり、なお悪いときにはセキュリティホールの原因になったりします。

$result=match($operator){BinaryOperator::ADD=>$lhs+$rhs,};// BinaryOperator::SUBTRACTを渡すと例外が発生する

match式はどのcaseにも当てはまらなかった場合はUnhandledMatchErrorを発するので、間違いに早期に気付くことができます。

Miscellaneous

Arbitrary expressions

matchする条件を任意の式にすることができます。
比較条件はswitch同様上から順に判定され、マッチした以後の条件は評価されません。

$result=match($x){foo()=>...,$this->bar()=>...,// foo()がマッチしたらここは呼ばれない$this->baz=>...,// etc.};

Future scope

この項目は将来の予定であり、このRFCには含まれません。

Blocks

このRFCでは、match式の本文はひとつの式でなければなりません。
ブロックを許すかについては、別のRFCで議論します。

Pattern matching

パターンマッチングについても検討しましたが、このRFCには含めないことにしました。
パターンマッチングは非常に複雑であり、多くの調査が必要です。
パターンマッチングについては別のRFCで議論します。

Allow dropping (true)

$result=match{...};// ↓と同じ$result=match(true){...};

Backward Incompatible Changes

matchがキーワードreserved_non_modifiersとして追加されます。
以下のコンテキストで使用することができなくなります。
・名前空間
・クラス名
・関数名
・グローバル定数

メソッド名およびクラス定数としては引き続き使用可能です。

Syntax comparison

他言語でのmatch構文

Vote

投票は2020/07/03まで、投票者の2/3の賛成で受理されます。
2020/06/22時点では賛成20反対1となっていて、よほどの問題でも発生しないかぎり受理されるでしょう。

感想

switchでよく問題になっていた曖昧な比較やbreakし忘れといったミスが、構文レベルで不可能となります。
そのため、match式に従っておけばswitchに起因する問題はほぼ発生しなくなるでしょう。
またmatch全体が返り値を持ってるのも便利ですね。

そのかわり、case内部には1式しか書けないため、複数の変数値を変更したり入れ子にしたりといった複雑な処理を書くことは難しくなります。
また、あえてbreakを書かずに継続したい場合も面倒な書き方になります。

// 1ならfooとbarを、2ならbarだけ実行したいswitch($x){case1:foo();case2:bar();break;}// aftermatch($x){1=>foo()&&bar(),// これは可能?2=>bar(),};

{}で括って複数の文を書けるようにするかどうかは、アロー関数同様今後の課題となっています。

従って本RFCは、決してあらゆるswitchを置き換える構文ではなく、アロー関数のように一部のswitch文を置き換えることができる短縮構文という立ち位置になります。
しかし、よほど変なことでもしていないかぎり、大抵のswitch文はmatch式に置き換えることができると思います。
安全性のためにも、今後はできるだけmatch式を使っていくとよいでしょう。

PHP8.0.0α1がリリースされたのでさっそくJITの威力を体感する(した)

$
0
0

2020/06/25、PHP8.0.0α1がリリースされました
PHP8系の初のバージョンです。
ただし名前のとおりα版であり、まだまだ実環境で使えるものではありません。
今後は2020/08/04にフィーチャーフリーズ、即ち新機能の取り込みが終了し、その後はβやRCで徐々に完成度を高めながら、2020/11/26に正式版がリリースされる予定です。

そんなわけでPHP8の目玉、JITの性能を試してみることにしましょう。

今回はXAMPPに導入してWebサーバとして動かすことができなかったので、以下のベンチマークはコマンドラインで実行した結果となります。
きっとそのうちXAMPPも対応してくれるはず。

インストール

QA ReleasesからVS16 x64 Thread Safeのzipをダウンロード。
適当なディレクトリに解凍。
php.ini-developmentphp.iniにコピー。

php.iniを変更。
memory_limit = 1024Mにする
date.timezone = "Asia.Tokyo"にする
extension_dir = "ext"のコメントアウトを外す

ベンチマークの設定

デフォルトの設定
・上の『php.iniを変更』のまま。ほぼ初期状態。

opcache有効の設定
・『デフォルトの設定』に対して、以下を追加する。
zend_extension=opcache
opcache.enable=1
opcache.enable_cli=1
opcache.optimization_level=0x7FFFBFFF

JIT有効の設定
・『opcache有効の設定』に対して、以下を追加する。
opcache.jit_buffer_size = 128M

ベンチマーク結果

プログラムは最後に載せておきますが、JITのRFCで使われていたマンデルブロ集合を計算するやつです。
使用したCPUはIntel i7-9700 3.00GHz。
測定結果の単位は秒です。

デフォルトopcache有効JIT有効参考7.4.7
0.8147550.3555850.1061900.960383
0.8182600.3569070.1069280.938955
0.8227460.3537990.1060610.940920
0.8172020.3534230.1067680.951347
0.8193910.3535740.1061170.936791

本当かよ?????????

まずPHP7.4.7からPHP8にアップデートするだけで処理時間が1割削減されています。
ただでさえ新機能てんこ盛りだってのに、そのうえ速度も上がるとかどうなってるんだPHP8。

次いでopcacheを有効にすると処理時間が半分になります。

最後にJITを有効にしたら、処理時間がopcache有効状態の30%になりました。
30%縮まりました、ではありません。
なんだこれ。

ということで、JITを有効にするだけで、処理速度がPHP8デフォルト設定の13%になりました。
どういうことかというと、元々1分かかっていた処理が8秒で終わるようになります。
足枷を外したとかいうレベルじゃねーぞ。
これ本当に計算してるのか?
計算結果が固定値だから結果だけどこかに保存してるとかじゃないよな?

しかもこれ、opcacheやJITの設定はほぼ初期値で、とりあえず有効にしただけという状態ですからね。
チューニングすればさらに早くなることでしょう。
面倒なので今回はそこまでやってませんが。

プリロード

PHP7.4.0で入ったプリロードで更なる高速化を図ってみましょう。

opcache.preload="path/to/preload.php"を設定
preload.phpの中身はopcache_compile_file('path/to/mandelbrot.php');

Error Preloading is not supported on Windows

はい。

実はWindowsではプリロードを使えません。

PHP7.4.0リリース時点ではWindowsでもプリロードを使えていたのですが、その後PHP7.4.2で提供が中止されてしまいました。
実はWindowsのプリロードはASLRのせいで本当のプリロードじゃないんだとかいうことらしいですがよくわかりません。

そんなわけで*nix勢あとよろ。

感想

あくまでCPUだけをがりがり使うプログラムに対しての検証結果です。
ファイルやデータベースへのアクセスが多くなる一般的なプログラムについては、また異なる結果になるでしょう。
またLaravelなど普通のWebアプリがどの程度高速化されるかも、今回は調べられていません。

とはいえ、そうはいってもさすがにこの結果は驚異的です。
もはや環境を作れないので試していませんが、PHP5.6からPHP7で速度が倍以上になったという過去もあります。
全部合わせるとPHP8が処理にかかる時間はPHP5の5%とかです。
PHP8はもはや、PHP5時代とは別次元の速度を手にいれました。

ベンチマークプログラム

マンデルブロ集合を計算するやつのほぼコピペです。

実行はコマンドラインからpath/to/php8/php.exe path/to/mandelbrot.phpとするだけ。

mandelbrot.php
define("BAILOUT",16);define("MAX_ITERATIONS",5000);// 1000だと早すぎたのでclassMandelbrot{publicfunction__construct(){$output='';$d1=microtime(1);for($y=-39;$y<39;$y++){for($x=-39;$x<39;$x++){if($this->iterate($x/40.0,$y/40.0)==0){$output.='*';}else{$output.=' ';}}$output.="\n";}$d2=microtime(1);$diff=$d2-$d1;echo$output;// 出力は最後にまとめたprintf("\nPHP Elapsed %0.6f\n",$diff);}publicfunctioniterate($x,$y){$cr=$y-0.5;$ci=$x;$zr=0.0;$zi=0.0;$i=0;while(true){$i++;$temp=$zr*$zi;$zr2=$zr*$zr;$zi2=$zi*$zi;$zr=$zr2-$zi2+$cr;$zi=$temp+$temp+$ci;if($zi2+$zr2>BAILOUT){return$i;}if($i>MAX_ITERATIONS){return0;}}}}$m=newMandelbrot();

歴史上もっともお勧めのスタートアップ本 25選

$
0
0

Original article:https://www.daolf.com/posts/best-startup-books/

みなさんスタートアップやってますか?
私はやってません。
はて、スタートアップといっても何をどうすればいいのでしょうか。

ブロガーやその他の人々がお勧めしている本を読めば間違いないでしょう。きっと。
世界に羽ばたくためにも、世界中で読まれているスタートアップ本を読めばいいに違いありません。

ということで以下はPierre de Wulf( Twitter / GitHub / Webサイト )による記事、The 25 most recommended startup books of all timeの日本語訳です。

なお、Amazonへのリンクは元記事のままであり、和訳にあたり変更などはしていません。

The 25 most recommended startup books of all time

インターネット上には、これがスタートアップのための必読書の決定版であると主張するリストが無数に存在します。
そしてそれらのリストを見比べてみると、ほとんどが同じであり一部の本だけが異なっているように見えました。

スタートアップについて本当に最も推奨される本は何なのかを知りたかったので、私はこれを作りました。
インターネット上で見つけた、208以上のリストと、4000ほどのお勧めを集計しました。
私の知る限り、このリストが、この種のテーマに関する最も完全なリストです。

Disclaimer:この記事を作るのに数えきれないほどの時間を費やしたので、その時間を費やすことが有益な収益源になり得るか確認するため、Amazonのアフィリエイトリンクをはっています。あるいはならないか🤷‍♂️.

さっさと結果を見たいのであればこちらから結果に飛んでください。
集計に使った方法を知りたいというなら、少々お時間をください。

Methodology

単純に、Googleに"ベストなスタートアップの本"、"ベストな起業家の本"といったクエリを幾つか尋ね、そしてその後それら全てのページを集計しました。
この作業にはScrapingBeeというスクレイピングAPIを使用しています。

重複などを除外しても300近くのリストが残りました。
さらにタイトルから一目で不要とわかるリストを削除しました。

・特定の著者だけをまとめている
・特定のトピックに焦点を当てている(例:暗号起業家の為のベスト本)
・無料本のリスト
・QuoraやRedditのコピー

最終的に254個のHTMLファイルが残りました。
ブラウザで全てのファイルを開いて、Chromeインスペクタを開いて、記事中の書籍のタイトルに一致するセレクタを探して、タイトルを収集しました。
この作業には2時間、1ファイルあたり30秒ほどかかりました。

そして、収集したHTMLページからCSSを抽出するための大きなJSONファイルができあがりました。

x1.png

PythonとBeautiful soupを使って、CSSセレクタにマッチしたDOM要素内のテキストを抽出しました。
結果膨大な本のリストができあがり、後処理をしないと使い物になりませんでした。

x2.png

最も頻繁に引用されたスタートアップの本を見つける為には、結果をノーマライズする必要がありました。
たとえば"7 habits of highly effective people"という本は、"Seven habits of highly effective people"とか"7 habits for highly for effective people"といった微妙に異なるタイトルで別々に集計されていました。

さらに加えて、"{書名} by {著者}"や"{書名} - {著者}"のような書式のぶれが厄介でした。

最終的に、以下のようなシンプルなPythonで処理しました。

defclean_link(link):link=link.encode().decode('ascii',errors='ignore')link=link.replace("'",'')link=link.lower()link=' '.join([wforwinlink.split(' ')ifwnotin['the','a']])link=link.split('by')[0]link=link.split(':')[0]link=link.split('(')[0]link=' '.join(link.split())link=link.replace('-','_')link=''.join([cforcinlinkifc.isalpha()orc=='_'orc==' '])link=link.strip()link=link.replace(' ','_')link=''.join([cforcinlinkifc.isalpha()orc=='_'])returnlink

その後は手動で整形しました。

そして、最終的に以下のようなリストが完成しました。

x3.png

これでようやく、最もお勧めの本を計算できるようになりました。
全ての結果はこのリポジトリで確認することができます。
それでは集計結果を見ていきましょう。

The 25 most recommended startup books of all-time.

25. Delivering Happiness: A Path to Profits, Passion, and Purpose by Tony Hsieh (7.6% recommended)

25.jpg

ZapposのCEOであるTony Hsiehが、ビジネスと人生で学んだ様々な教訓を語ります。
LinkExchangeやZapposなどを渡り歩き、ミミズの養殖から始まりピザビジネスの運営まで、様々なことが書かれています。
速いペースで地に足の付いた文章で、企業文化の大きな違いが成功を収めるための強力なモデルであることを示しています。
あなたの周りの人々を幸せにすることが、あなた自身をも大きく幸せにしてくれます。

24. Shoe Dog: A Memoir by the Creator of Nike by Phil Knight (7.6% recommended)

24.jpg

Knightが最初の勝利を収めるまで、彼と彼の夢の間に立ちはだかった多くのリスクと困難と挫折を詳細に語っています。
社会不適合者と求道者の寄せ集めが、固い絆で結ばれた兄弟になるまでの、初めてのパートナーや社員との関係を振り返っています。
共通の使命とスポーツに対しての深い信念を共にした彼らは、全てを変えるブランドを築き上げました。

Phil KnightはNikeの共同創業者。

23. Purple Cow: Transform Your Business by Being Remarkable by Seth Godin (8.1% recommended)

23.jpg

あなたは紫の牛であるか、そうでないかのいずれかです。
あなたは目立っているか、誰にも気付かれないかのいずれかです。
どちらかを選んでください。
Apple、Starbucks、Dyson、そしてPret a Mangerの共通点は何でしょう?
彼らはどのようにして盛大に成長し、偉大なブランドになる最後の一歩を踏み出したのでしょうか、
価格(Pricing)、販促(Promotion)、宣伝(Publicity)といった、かつてマーケティングに使われていたPたちは、もはや機能していません。
広告の黄金時代は終わりました。
今やここに、新しいP…紫の牛(Purple Cow)を導入すべき時期なのです。
"Purple Cow"はとは驚くべき何か、直観に反する、刺激的で信じられない何か、を表します。
Seth Godinは、あなたが作る全てのもの、あなたが行う全てのことに、本当に注目すべき何らかの"Purple Cow"を置くべきだ、と主張しています。
この本は、マーケティングするべき価値のある商品やサービスを作りたいひとたち全員に向けた檄の書です。

22. Outliers: The Story of Success by Malcolm Gladwell (8.1% recommended)

22.jpg

Malcolm Gladwellによって書かれたこの驚くべき書は、我々を"異常者"の世界へと導く知識の旅に連れ出します。
"異常者"とはそう、世界最高の頭脳、最も有名な人、最も成功した人たちです。
彼は質問します。
いったい彼らは我々と何が違うのでしょう?

我々は、成功した人たちがいったいどのような者であるかということにばかり気を向け、彼らがいったいどこから来たのかということに気を払いません。
それはすなわち文化であり、家族であり、世代であり、そして彼らが育ってきた特異な環境です。
この旅の途中で彼は、ソフトウェア億万長者の秘密、伝説のサッカー選手になるために必要なこと、アジア人が数学が得意な理由、ビートルズが偉大なロックバンドになった理由を解き明かします。

21. The Power of Habit: Why We Do What We Do in Life and Business by Charles Duhigg (8.1% recommended)

21.jpg

数々の受賞歴を持つビジネスレポーターCharles Duhiggが、何故我々は習慣を持っているのか、そしてそれをどのように変えていくのかについて、スリリングな科学的発見を解説します。P&Gの役員室からNFLのサイドラインに、そして公民権運動の最前線まで、人間の性質やポテンシャルを理解するための膨大な量の情報が、我々を魅了する物語へと導きます。
そしてその核心で、この本は爽快な主張を行います。
定期的に運動すること、減量すること、より生産性を高めること、そして成功を収めること、これは全て習慣の働きを理解することです。
Duhiggが示したように、我々はこの新しい科学を利用することによって、ビジネスやコミュニティ、そして人生を変化させることができます。

20. Founders at Work: Stories of Startups' Early Days by Jessica Livingston ( 8.6% recommended)

20.jpg

起業家、イノベーター、あるいは世界最高のテクノロジーを産み出す科学と原動力に魅了されている人にとって、この本は魅力的な知恵と洞察を提供してくれます。
FaWは有名なテクノロジー企業の創業者へのインタビューを集めたもので、スタートアップの初期に何が起こったかについて描かれています。

19. Hooked: A Guide to Building Habit-Forming Products by Nir Eyal (8.6% recommended)

19.jpg

何百万という人々に手にとってもらいたい製品を作ろうとしているときに、この本はシンプルでありながらも非常に有用なモデルを紹介してくれます。
すぐに読めて(わずか140ページ)、要点を押さえた内容で、コンセプトとデザインの課題に新風をもたらしてくれました。
ユーザの行動をモデル化し、エンゲージメントを得るために必要だったのに見逃していた領域を把握するために、この本を利用しました。

この本のもうひとつの大きな価値は、(Twitter・Facebook・Pinterest・Instagramなどから)我々が毎日受けているとっかかりの詳細な分析です。
毎日をユーザ目線でよく観察することで、構造化された方法でコインの反対側を見通すことができるようになります。

18. The Innovator's Dilemma: When New Technologies Cause Great Firms to Fail by Clayton M. Christensen (9.1% recommended)

18.jpg

多くの企業が新しいイノベーションの波に乗り遅れてしまう理由を、Christensenが解説しています。
いかなる業界においても、経営者が伝統的な商習慣を捨てる方法とタイミングを知らなければ、既に確固たる地位を築いている製品で成功している企業もいずれ押しのけられてしまいます。

この本は、大手企業の成功と失敗の両方をガイドとして提供し、破壊的イノベーションを活用するための一連の手法を提供しています。

17. Art of the Start 2.0: The Time-Tested, Battle-Hardened Guide for Anyone Starting Anything by Guy Kawasaki (9.1% recommended)

17.jpg

Guy Kawasakiはビジネス界で最も独創的で不遜な戦略家の一人であり、その20年の経験から、何かを始める人のための必須のガイドを提供します。
1980年代のAppleにおいて、彼は一般消費者をエバンジェリストに進化させ、世界的企業に育てることに後見しました。
さらにベンチャーキャピタルであるGarage Technology Venturesの創業者CEOとして、何十社もの企業で彼のアイデアの実地テストを行ってきました。
そしてベストセラーのビジネス書や記事の著者として、スタートアップの夢を実現しようとしている何千もの人々にアドバイスをしてきました。

16. Tools of Titans: The Tactics, Routines, and Habits of Billionaires, Icons, and World-Class Performers by Tim Ferriss (9.1% recommended)

16.jpg

過去2年間、私はPodcast『The Tim Ferriss Show』で200名以上の世界クラスの人々にインタビューしてきました。
その対象はスーパーセレブ(Jamie Foxx、Arnold Schwarzeneggerなど)、アスリート(リフティング、体操、サーフィン選手など)をはじめ、伝説の特殊作戦部隊司令官や闇市場の生化学者まで多岐にわたっています。
ほとんどのゲストが、数時間のインタビューに応じてくれたのは初めてのことです。
この独自性により、『The Tim Ferriss Show』はビジネスジャンルとして初めて1億ダウンロードを超えたPodCastになりました。

15. Influence: Science and Practice by Robert B. Cialdini (9.6% recommended)

15.jpg

この書は、学術的な研究を含みながらも物語形式で書かれています。
Cialdiniは、営業マン・資金調達者・アドバイザーの仕事から得た、相手に如何にして"はい"と言わせるかのテクニックと戦略を、実験的な仕事から得た証拠と組み合わせました。
授業でも広く使われ、ビジネス界で活躍する人たちに愛読されているInfluence待望の改訂版は、説得力の強さを思い知らされます。

14. Traction by Gabriel Weinberg and Justin Mares (9.6% recommended)

14.jpg

この本は、あなたのビジネスに適した顧客の基盤を構築するための19の方法を教えてくれます。
Jimmy Wales (Wikipedia)、Alexis Ohanian (reddit)、Paul English (Kayak)、Dharmesh Shah (HubSpot)といった40人以上の成功したフォーリナーへのインタビューに基づいています。
たとえば以下のような学びを得ることができます。
・オフライン広告や、競合他社が使用していないチャネルを見つけて利用する。
・ターゲットを狭く絞った分野のメディア取材を受けることで、より多くの顧客を獲得する。
・自動更新をあえて不定期にすることで、メールマーケティングなどの効果を高める。
・オンラインツールやリサーチにより、検索エンジンの順位や広告効果を向上させる。

13. Rich Dad Poor Dad by Robert T. Kiyosaki (12.9% recommended)

13.jpg

本書は二人の父親、実の父親と、その親友である金持ちの父親、に育てられたRobertの物語です。
そして彼を形作った、二人の父親のお金と投資についての姿勢の話です。
あなたが金持ちになるためには高収入を得る必要があるという固定観念を壊し、あなたがお金のために働くこととお金があなたのために働くことの違いを解説します。

12. The 7 Habits of Highly Effective People by Stephen R. Covey (12.9% recommended)

12.jpg

有史以来書かれてきた中でも最も刺激的でインパクトのある書籍のひとつである本書は、25年にわたって読者を魅了してきました。
この本は社長、CEO、教育者に親たち、端的に言うと世界中のあらゆる年齢や職業、の数百万人の人生を変えてきました。
Stephen Coveyによる古典の25周年記念版は、彼の時代を超えた知恵を記念し、我々が大きな目的を持って生きていくことを勧めています。

11. Rework by Jason Fried and David Heinemeier Hansson (14% recommended)

11.jpg

ほとんどのビジネス書には、全く同じ古いアドバイスが書かれています。
ビジネスプランを書き、競合を研究し、投資家を探し、なんとかかんとか。
そんな本を探しているのであれば、かわりにこの本を手に取りましょう。

この本を読めば、何故計画が有害なのか、何故外部の投資家が不要なのか、そして何故競合を無視した方がいいのかを知ることができます。
あなたが必要だと考えているよりも、あなたが本当に必要としているものは少ないのです。
ワーカホリックになる必要はありません。
人員を増やす必要もありません。
ペーパーワークやミーティングで時間を浪費する必要もありません。
オフィスも必要ありません。
それらは全て余計なものです。

あなたが本当にする必要のあることは、口を閉じて手を動かし始めることです。
この本はその方法を教えてくれます。
あなたはより生産的になり、破産せず有名になり、直感的ではないアイデアに多くのインスパイアを得るでしょう。

わかりやすい言葉と容易なアプローチは、これまで自分で自分を律することを夢見てきた人たちのための完璧な脚本です。
ハードコアな起業家、中小企業のオーナー、嫌いなジョブを嫌々こなしている人、ダウンサイジングの犠牲者、飢えたくないアーティストは、この本から貴重な指標を得ることができるでしょう。

10. Start with Why by Simon Sinek (14.4% recommended)

10.jpg

Sinekは根源的な疑問から始めました。
何故、ある人や組織は、他の人よりも革新的で、影響力があり、収益性が高いのだろうか?
何故、一部の人々や組織は、顧客や従業員からの支持が高いのだろうか?
たとえ成功者といえど、何度も何度も成功を繰り返すことができる人は何故少ないのだろうか?

Martin Luther King Jr.とSteve Jobs、そしてWright Brothersにはほとんど共通点がありませんが、彼らはみなWHYから始まりました。
彼らは、製品やサービス、行動やアイデアの背後にあるWHYを理解しない限り、人々は実際にそれらを購入することがないことに気付いていました。

世界で最も影響力を及ぼしているリーダーたちは、全て同様に考え、行動し、コミュニケートしていたことをSTART WITH WHYは解き明かしています。
そしてそれは、大多数の人々がやっていることとは正反対です。
Sinekはこの発想をGolden Circleと名付けました。
それは組織を構築し、ブームを導き、人々を惹き付けるための枠組みを提供します。
そしてそれはいつも、WHYから始まります。

9. Think and Grow Rich by Napoleon Hill (14.4% recommended)

09.jpg

この本は、"すべての自己啓発本の祖"と呼ばれています。
何が勝者を作るのか、を大胆に問いかけた初めての本でした。
答えを追い求め、その答えを聞いた男、Napoleon Hillは今では世界の勝者の一人です。
成功の教師の中でも最も有名である彼は、彼の哲学である成功の法則を生み出すため、大金と半生を費やした内容をこの本に力強くまとめています。

1937年に出版されたこの本のオリジナルでは、Andrew Carnegie、Thomas Edison、Henry Fordといった当時の大富豪の物語から彼は成功の法則を導き出します。
更新版では、世界的に有名な著作家、講師、人事管理コンサルタントであり、Hillの思想を取り入れたエキスパートであるArthur R. Pellの手によって、Bill Gates、Mary Kay Ash、Dave Thomas、Sir John Templetonといった現代の億万長者がどのように富を手に入れたのかについての逸話が巧みに織り交ぜられました。
また時代遅れな例や難解な用語は、現代の読者にも理解できるよう書き改められています。

8. Good to Great: Why Some Companies Make the Leap and Others Don't by Jim Collins (14.9% recommended)

08.jpg

5年前にJim Collinsは、『"良い"会社が"偉大な"会社になれるのかだろうか。そうだとすればどのようにすればいいのか。』という疑問を持ちました。
本書においてJim Collinsは、それは可能であると結論づけましたが、しかし同時に銀の弾丸も存在しないことも発見しています。
Jim Collinsと彼の研究チームは1435の会社を調査し、時間経過で業績が大幅に改善された企業を探し、その理由の探求を始めました。
最終的に彼らはFannie Mae、Gillette、Walgreens、およびWells Fargoなどの11企業に狙いを定め、そして企業が成功するために必要とされていた従来の概念を覆す共通の特徴を発見しました。

"良い"企業から"偉大な"企業への飛躍には、耳目を集めるCEOや最新のテクノロジーは不要で、革新的なマネジメント、あるいは精緻なビジネス戦略すらも必要ありません。
真に偉大で希有な企業の柱には、ある一定の規則に従って考え、行動する人々を見つけ、育てるという企業文化がありました。
本書は、"偉大な"企業の例とそうではない企業の例を多数出しながら、あらゆる組織が規範とすべき十分に合理的なロードマップを提供しています。
"良い"から"偉大な"になるために、マネージャやCEOは目を通しておき、そして来たるべき時に再読すべき本です。

7. The E-Myth Revisited: Why Most Small Businesses Don't Work and What to Do About It by Michael E. Gerber (15.3% recommended)

07.jpg

驚異的なベストセラーの改訂版である本書は、ビジネスを始めることについての神話を払拭してくれます。
ビジネスコンサルタントである著者Michael E. Gerberは、長年の経験から得たその鋭い観察眼で、一般的な思い込みや期待だけではなく、ときには技術的な専門的知識さえもがビジネスを成功させる道を邪魔してしまう可能性があることを指摘しています。
Gerberは、生活をビジネスに応用する手段を提供します。
またフランチャイズでなかったとしても、フランチャイズの教訓をどのようにビジネスに適用するか、ということも示します。
最も重要なこととしてGerberは、見過ごされてしまいがちな仕事に"work in"することと仕事に"work on"することの違いを明らかにします。

6. The 4-Hour Workweek by Timothy Ferriss (15.8% recommended)

06.jpg

今一生懸命働いて定年後に休む、そんな昔ながらの古い概念は捨ててしまいましょう。
この先行き不透明な経済状況の中では特にです。
あなたの夢はラットレースからの脱出でしょうか、ハイクラスの世界旅行でしょうか、はたまた何もせずに月5桁ドルの収入を得ることでしょうか。
いずれにせよ、このThe 4-Hour Workweekはその将来への青写真となります。

5. How To Win Friends and Influence People by Dale Carnegie (22.5% recommended)

05.jpg

Dale Carnegieによる信頼性の高い確固とした助言は、数えきれないほどの人々の人生を、成功という梯子の上に運び上げてきました。
最も画期的で、時代を超えたベストセラーのひとつであるこの本は、人を動かすための方法を教えてくれます。
・人に好かれる六原則
・人を説得する十二原則
・人を変える九原則
他にも多くのことが書かれています。
1500万部を超える、21世紀の必読書です。

4. The $100 Startup: Reinvent the Way You Make a Living, Do What You Love, and Create a New Future by Chris Guillebeau (24% recommended)

04.jpg

著者のChrisはいまだ30代前半であるにもかかわらず、既に175か国以上を訪問しており、そして地球上のすべての国を回ろうとしています。
しかしながら彼は一度も就職したこともなければ普通に給料をもらったこともありません。
彼にはアイデアをお金に変える特別な才能があり、そして手に入れたお金で冒険の旅を送っているのです。

Chrisのような人は他にも大勢います。
伝統的な雇用体制から脱却し、自分にとって意味のあることを追求するための時間と収入を作り出す者たちです。
収入と情熱のあいだに収まるために、今やっていることを放り出す必要はありません。
少量の時間とお金をコミットして、ベンチャーとしてミニマムで始めることができます。
そして、成功すると確信してから本気を出せばいいのです。

3. The Hard Thing About Hard Things by Ben Horowitz (24.5% recommended)

03.jpg

Andreessen Horowitzの共同設立者であり、シリコンバレーで最も尊敬されている企業家であるBen Horowitzによる、スタートアップを設立し運営していくために必要なアドバイスです。
彼のブログを元にした、ビジネススクールでは決して教わらない、非常に困難な問題に立ち向かうための実用的な知恵です。

ビジネスを始めることがどれだけ素晴らしいことかを話す人は数多いですが、実際にそれがどれだけ難しいことであるかを正直に語る人はほとんどいません。
Ben Horowitzはリーダーが日々直面する問題を分析し、そしてテクノロジー企業の起業、管理、売却、買収、投資、監督などから得られた教訓を惜しみなく共有しています。
ラップが大好きな彼が、好きな曲の歌詞から教訓を学び、友人を買い越し、競争相手を買収し、CEOとしてのメンタリティを育て維持し、現金化の適切なタイミングを見極めることといった様々な話を率直に語っています。

Horowitzの個人的な、しばしば失敗した経験から導き出された本書には、彼のトレードマークであるユーモアとストレートなトークが満載で、ベテランの起業家だけではなく、これから新しく起業を目指す人にとっても貴重な価値を持っています。

2. Zero to One by Peter Thiel (29.3% recommended)

02.jpg

我々の時代に隠されている大きな秘密は、未知のフロンティアを開拓して新たな発明を作り出す余地はまだまだ存在するということです。
伝説の投資家であり企業家であるPeter Thielが、それら新しいものを見つけるための非凡な方法を本書で示しています。

本書はまず、我々はテクノロジーの停滞の時代に生きているのであるという逆説から始まります。
たとえキラキラのモバイルデバイスに囲まれていたとしてもです。
情報技術はたしかに急激に進歩していますが、しかしコンピュータやシリコンバレーだけが進歩しているわけではありません。
進歩は、どの業界でもどんな分野でも起こります。
そのためには、全てのリーダーがマスターしなければならない最も重要なスキルを持っていなければなりません。
すなわち、自分で考える方法を学ぶことです。

他の誰かが既にやっていることをやるということは、世界を1からnに変えるということであり、その技術をより身近なものにしてくれるでしょう。
しかし、まだ誰もしてないことをするということは、0を1にすることです。
次のBill GatesはOSを作らないだろうし、次のLarry PageやSergey Brinは検索エンジンを作らないことでしょう。
明日のチャンピオンは、今日の市場で競争して勝つことを狙ってはいません。
彼らのビジネスはユニークなので、競い合うこと自体が行われません。

本書はアメリカの未来の進歩を楽観し、新たなイノベーションの考え方を示してくれます。
それは、思いもよらないところに価値を見出すために疑問を投げかけることから始まります。

1. The Lean Startup by Eric Ries (44.7% recommended)

01.jpg

Eric Riesは、スタートアップを『極めて不確実性の高い状況下で、新しいものを創造することに専念する組織』であると定義しました。
これは、たった一人でガレージを仕事場にしている人から、Fortune500掲載企業の熟練プロ集団にも当てはまることです。
彼らに共通しているものは、不確実な霧の向こうを見通し、持続可能なビジネスとして成功させようという意志です。

資本効率と社員のクリエイティビティを、いずれも効率的に育てていくのがLean Startupのアプローチです。
製造業にヒントを得たこのアプローチは、検証による学びを重視しています。
迅速な調査、製品の開発サイクルを短縮させるための直感的ではない方法、恣意的な測定基準ではなく実際の進捗を調べることなどで、顧客が本当に求めているものを見つけ出します。
これにより、企業は計画を少しづつ、秒進分歩で俊敏に軌道修正していくことができるようになります。

本書は、精巧なビジネスプランを立てて時間を浪費するよりも、手遅れになる前にビジョンを継続的にテストして修正して適応させていく方法を提供します。
企業がこれまで以上にイノベーションを起こす必要のある時代に、成功するスタートアップを立ち上げ、管理するための科学的アプローチです。

Conclusion

いくつかの順位には驚いたかもしれませんが、これらの多くについては既に聞いたことがあるでしょう。

このリストを作るにあたり、いくつか気付いたことがありました。

・複数冊が入ったのはTim Ferrissだけでした。
・聖書が1回紹介されていました。
・最も多く出てきた伝記はWalter IsaacsonによるSteve Jobs biographyで、6%の人が勧めていました。

この記事を楽しんでいただけたら幸いです。

正直に言って、この記事を書くのにすごい時間がかかりました。
この気に入ってくれたらぜひリツイートしてください。
でも、このためだけにアカウントを作ったりしないでね。

dev.toのコメント欄

「素晴らしいリスト!」
「リーンスタートアップが1位←知ってた」
「4 hour work weekのおかげでスタートアップの世界に入ったよ。」
「どこの誰が紹介していたかの一覧がほしい。」
「リストを自動的に抽出するボットとか楽しそう。」

感想

訳しておいてなんだけど、私は自己啓発本に全く興味が無いので一冊も読むことはないです。

まあそんな奴は少数派なので置いておくとして、多くの人が紹介している本は良い本に違いないというマクドナルド理論に従うと、The Lean Startupが最も優れた書籍と言えるでしょう。
言えるでしょうというか実際めちゃくちゃ評判もいいし、それどころか本書一冊で方法論の一大ジャンルを築くまでになっていますからね。
それだけ世界に多くの影響を与えた、重要な書籍であることは間違いありません。
2位以下の本も、その多くは100万部を超えるようなベストセラーばかりです。
読めば自らのためになることは確定的に明らかです。

なお、どの書籍も多くのサイトで紹介される有名なものばかりだけあって、何れも日本語訳が出版されています。
興味があるなら探してみるのもいかがでしょう。
日本語にしてみると、あっこれ聞いたことある、って本もわりとあったりしますよ。

【PHP8.0】PHPに名前付き引数が実装される

$
0
0

全ぺちぱーが待ち望んでいた機能がついに来まするよ。

変な関数
functionhoge($foo=null,$bar=false,$baz=0,$qux=''){}

こんな関数があったとして、4番目の引数だけ変更したい、他はデフォルトのままでいいという場合、現在のPHPではいちいちデフォルト値を調べて与えないといけません。

めんどう
hoge(null,false,0,'fuga');

この問題解決のために、これまで幾つものRFCが立ち上がっては却下されてきました。
しかし今回この問題に、ついに決定的な解決策が提供されました。

名前付き引数
hoge(qux:'fuga');

C#とかPythonとかの名前付き引数とだいたい同じです。

以下は該当のRFC、Named Argumentsの日本語訳です。

Named Arguments

Introduction

名前付き引数では、引数の順番ではなく、引数の名前に基づいて関数に引数を渡すことができます。
これにより、引数の意味が自己文書化され、引数の順序に囚われないようになり、デフォルト値を任意にスキップすることができるようになります。

// これまでarray_fill(0,100,50);// 名前付き引数array_fill(start_index:0,num:100,value:50);

名前付き引数に渡す引数の順番は任意です。
上の例では関数シグネチャと同じ順番でしたが、異なる順番で渡すことも可能です。

array_fill(value:50,num:100,start_index:0);

名前付き引数と通常の引数を組み合わせることも可能であり、オプション引数の一部のみを順番に関係なく指定することができます。

htmlspecialchars($string,double_encode:false);// ↑同じ↓htmlspecialchars($string,ENT_COMPAT|ENT_HTML401,'UTF-8',false);

What are the benefits of named arguments?

Skipping defaults

名前付き引数の明白な利点のひとつは、上記htmlspecialcharsの例に見て取れます。
変更したい引数までの間にある引数すべてにデフォルト値を指定する必要がなくなります。
名前付き引数を使用すると、変更したい引数だけを直接指定することができます。

これはskipparamsのRFCと似ていますが、名前付き引数のほうが意図がより明確になります。

htmlspecialchars($string,default,default,false);// どっちがわかりやすい?htmlspecialchars($string,double_encode:false);

たまたまhtmlspecialcharsの引数を全て暗記でもしていないかぎり、最初の例では最後のfalseが何を表しているのかわかりませんが、後者の例であればその意味が見ればわかります。

Self-documenting code

引数を省略しない場合でも、コードを自己文書化するメリットはあります。

array_slice($array,$offset,$length,true);// 同じarray_slice($array,$offset,$length,preserve_keys:true);

2つめの例がなかったとしたら、array_sliceの4番目のtrueが何を意味しているのかきっとわからないでしょう。

Object Initialization

プロパティ宣言を簡潔にするオブジェクト初期化子のRFCからひとつ例を取ってみます。

// Part of PHP AST representationclassParamNodeextendsNode{publicfunction__construct(publicstring$name,publicExprNode$default=null,publicTypeNode$type=null,publicbool$byRef=false,publicbool$variadic=false,Location$startLoc=null,Location$endLoc=null,){parent::__construct($startLoc,$endLoc);}}

コンストラクタは特に、引数の数が通常よりも多く、その順番は特に重要ではなく、一般的にデフォルト値がついていることが多いです。
オブジェクト初期化子はクラスの宣言をよりシンプルにしますが、クラスのインスタンス化は全くシンプルにしません。

オブジェクトの構築をより容易なものにしようとする試みは、Object InitializerCOPAなど、これまで幾度も行われてきました。
しかし、コンストラクタやprivateプロパティとの相性が悪く、言語にうまく統合することができなかったため、これらは全て却下されてきました。

名前付き引数は、既存の言語セマンティクスとうまく統合され、副作用としてオブジェクトの初期化問題を解決します。

newParamNode("test",null,null,false,true);// ↑は↓こうなるnewParamNode("test",variadic:true);newParamNode($name,null,null,$isVariadic,$passByRef);// ↑↓どっちだったっけ?newParamNode($name,null,null,$passByRef,$isVariadic);// どっちだったとしても大丈夫newParamNode($name,variadic:$isVariadic,byRef:$passByRef);// 順番なんてもはやどうでもいいnewParamNode($name,byRef:$passByRef,variadic:$isVariadic);

オブジェクト初期化の際の名前付き引数の利点は、一見他の関数と同じですが、実際にはより利点が明確になるでしょう。

Type-safe and documented options

名前付き引数がなかったころの一般的な対策のひとつは、オプションを配列にすることです。
さきほどの例は、オプション配列を使うことで以下のように書き換えることができます。

classParamNodeextendsNode{publicstring$name;publicExprNode$default;publicTypeNode$type;publicbool$byRef;publicbool$variadic;publicfunction__construct(string$name,array$options=[]){$this->name=$name;$this->default=$options['default']??null;$this->type=$options['type']??null;$this->byRef=$options['byRef']??false;$this->variadic=$options['variadic']??false;parent::__construct($options['startLoc']??null,$options['endLoc']??null);}}// 使い方newParamNode($name,['variadic'=>true]);newParamNode($name,['variadic'=>$isVariadic,'byRef'=>$passByRef]);

これも動作しますし、現在すでに使用可能ですが、デメリットも多く存在します。

・せっかくのオブジェクト初期化子が利用できない。
・オプション配列は関数シグネチャに記載されないため、何が使えるかは実装やPhpdocを見ないとわからない。またPhpdocにはオプション配列を表記する統一方法がない。
・オプション配列の値の型は型宣言で検証することができない。
・よほど厳格にチェックしないかぎり、未知のオプションを渡してもそのまま動いてしまう。
・最初からオプション配列を導入していないかぎり、後からオプション配列の引数に切り替えることは難しい。

名前付き引数であれば、オプション配列と同程度のメリットを、デメリットなく受容することができます。

Attributes

Phpdocアノテーションにおいては名前付き引数の使用は既に広まっています。
アトリビュートのRFCではPhpdocアノテーションをファーストクラス言語機能に取り入れていますが、名前付き引数には対応していません。
すなわちアノテーションをアトリビュートに移行するためには、構造を変更しなければならないことを意味します。

たとえばSymfonyのRouteアノテーションは多くのオプショナル引数を受け取ります。
現在、アノテーションのアトリビュートへの移行は以下のようになります。

/**
 * @Route("/api/posts/{id}", methods={"GET","HEAD"})
 */publicfunctionshow(int$id){...}// ↑は↓こうならざるを得ない<<Route("/api/posts/{id}",["methods"=>["GET","HEAD"]])>>publicfunctionshow(int$id){...}

名前付き引数を導入することで、移行前と全く同じ構造でアトリビュートを指定することが可能になります。

<<Route("/api/posts/{id}",methods:["GET","HEAD"])>>publicfunctionshow(int$id){...}

ネストされたアノテーションのサポートが不十分なため完全に同じとまではいきませんが、移行をよりスムーズにすることはできます。

Proposal

Syntax

名前付き引数は、引数名にコロンを続けて値を渡します。

callAFunction(paramName:$value);

引数名として予約済キーワードも使用可能です。

array_foobar(array:$value);

引数名は固定値でなければならず、動的に指定することはできません。

// だめfunction_name($variableStoringParamName:$value);

定数もサポートされません。
なぜならfunction_name(FOO: $value)の変数名はそのままFOOなのか、それとも定数FOOの値なのかが区別できないからです。
引数名を動的に指定する別の方法については後述します。

技術的に実現可能な代替構文が幾つか存在します。

function_name(paramName:$value);// (1) RFCの提案function_name(paramName=>$value);// (2)function_name(paramName=$value);// (3)function_name(paramName=$value);// (3) 3のバリエーションfunction_name($paramName:$value);// (4)function_name($paramName=>$value);// (5)

以下の構文は、現在既に正しいコードであるため使用できません。

function_name($paramName=$value);

このRFCの旧バージョンでは、バリアント2の構文を使用していました。
しかし、実際に導入してみると問題が多く、人間に優しくないことがわかりました。
より良いかもしれない追加の構文については、Future Scopeセクションを参照してください。

Constraints

ひとつの関数で位置引数と名前付き引数の両方を指定することができますが、名前付き引数は位置引数の後ろに来なければなりません。

// OKtest($foo,param:$bar);// コンパイルエラーtest(param:$bar,$foo);

同じ引数を複数回渡すと例外が発生します。

functiontest($param){...}// Error: Named parameter $param overwrites previous argumenttest(param:1,param:2);// Error: Named parameter $param overwrites previous argumenttest(1,param:2);

ひとつめは同じ名前を2回指定している単純なミスです。
ふたつめは、位置引数と名前付き引数が両方とも同じ引数を指しているため、これもエラーです。

後述する可変変数を除いて、存在しない引数を指定すると例外になります。

functiontest($param){...}// Error: Unknown named parameter $parmatest(parma:"Oops, a typo");

Variadic functions and argument unpacking

可変長引数...$argsで定義された関数では、名前付き引数も$argsに集約されます。
名前付き引数は常に位置引数の後ろになり、順番は引数に渡された順です。

functiontest(...$args){var_dump($args);}test(1,2,3,a:'a',b:'b');// [1, 2, 3, "a" => "a", "b" => "b"]

引数アンパックは名前付き引数にも対応しています。

$params=['start_index'=>0,'num'=>100,'value'=>50];array_fill(...$params);

文字列キーの値は名前付き引数として扱われます。
整数キーは通常の位置引数として扱われ、整数値は無視されます。
整数でも文字列でもないキー(イテレータでのみ現れることがある)はTypeErrorになります。

引数アンパックも、名前付き引数は位置引数の後ろになければならないという制限に従います。
以下の呼び出しはどちらも例外になります。

// どちらもエラーarray_fill(...['start_index'=>0,100,50]);array_fill(start_index:0,...[100,50]);

さらに引数アンパックは、...の後ろに引数を追加することができないという、元からあった制限も受け継ぎます。

test(...$values,$value);// Compile-time error (as before)test(...$values,paramName:$value);// Compile-time error

可変長引数と引数アンパックのよくある使い方のひとつは、引数の転送です。

functionpassthru(callable$c,...$args){return$c(...$args);}

可変長引数も引数アンパックも既に名前付き引数をサポートしているので、このパターンは名前付き引数が導入されても動作は変わりません。

func_get_args() and friends

func_*()系の関数では、全ての引数が位置引数として渡されたかのように扱われ、未指定の引数はデフォルト値になります。
たとえば以下のようになります。

functiontest($a=0,$b=1,$c=2){var_dump(func_get_args());}test(c:5);// array(3) { [0] => 0, [1] => 1, [2] => 5 }// ↓と全く同じtest(0,1,5);

func_num_args()func_get_arg()も、func_get_args()と一致する動作となっています。
3つの関数は全て未知の名前付き引数を無視します。
func_get_arg()は値を返さず、func_num_args()はカウントしません。
未知の名前付き引数は直接値にアクセスした場合にのみアクセス可能です。

call_user_func() and friends

call_user_func()call_user_func_array()などの呼び出し転送を行う関数も、名前付き引数をサポートします。

$func=function($a='',$b='',$c=''){echo"a: $a, b: $b, c: $c\n";}// 全部同じ$func('x',c:'y');call_user_func($func,'x',c:'y');call_user_func_array($func,['x','c'=>'y']);

これらの呼び出しも他と同様の制限を受けます。
たとえば、名前付き引数の後ろに位置引数を書くことはできません。

call_user_func_array()は微妙に後方互換性がありません。
以前は、配列のキーは完全に無視されていましたが、今後は文字列キーが引数名として解釈されるようになります。

call_user_funcを基本形として、ReflectionClass::newInstance()ReflectionClass::newInstanceArgs()など類似の関数にも同じ対応が適用されます。

__call()

__invoke()と異なり、__call()__callStatic()マジックメソッドは名前付き引数の有無で動作を区別しません。
最大限の互換性を保つため、__call()は可変長引数と同様に引数の配列として渡します。

classProxy{publicfunction__construct(privateobject$object,){}publicfunction__call(string$name,array$args){// $name == "someMethod"// $args == [1, "paramName" => 2];$this->object->$name(...$args);}}$proxy=newProxy(newFooBar);$proxy->someMethod(1,paramName:2);

Attributes

アトリビュートも名前付き引数をサポートします。

<<MyAttribute('A',b:'B')>>classTest{}

通常の呼び出しと同様、名前付き引数の後に位置引数を渡すとコンパイルエラーになり、同じ名前の引数を複数回渡すとコンパイルエラーになります。

ReflectionAttribute::getArguments()は、位置引数と名前付き引数を可変長引数と同じ形式で返します。

var_dump($attr->getArguments());// array(2) {//   [0]=>//   string(1) "A"//   ["b"]=>//   string(1) "B"// }

ReflectionAttribute::newInstance()メソッドも、通常の呼び出しと同じ規則で名前付き引数をコンストラクタに渡します。

Parameter name changes during inheritance

現在のところ、引数名は継承の対象ではありません。
これは位置引数だけを使用する場合は合理的でした。
引数名は呼び出し側には無関係だからです。

名前付き引数ではここが変更になります。
継承したクラスが引数名を変更すると、名前付き引数の呼び出しが失敗する可能性があり、リスコフの置換原則に違反します。

interfaceI{publicfunctiontest($foo,$bar);}classCimplementsI{publicfunctiontest($a,$b){}}$obj=newC;// インターフェイスに従ったのにエラー$obj->test(foo:"foo",bar:"bar");// ERROR!

他の言語ではこの問題をどのように処理しているかを、ここで詳細に分析しています。
結果をまとめると、
・PythonとRubyは引数名の変更を許可し、呼び出し時にエラー
・C#とSwiftはオーバーロード。オーバーライドするとエラー。PHPはオーバーロードできないため真似できない。
・Kotlinは引数名変更時に警告を出し、呼び出すとエラー。

多くの言語が名前付き引数を後付けで実装しているため、既存のコードに影響を出さないように、引数名の違いを無条件で警告することは賢明ではないと考えられます。

このRFCでは、PythonやRubyのモデルに従うことを提案しています。
すなわち、PHPは継承時の引数名変更を許容しますが、その結果、引数名を変更したメソッドを呼び出すと実行時例外が発生する可能性があります。
静的解析ツールやIDEは、引数名の不一致に警告を出し、その警告を抑制可能にすることが推奨されます。

これは、名前付き引数が多くのメソッドにはあまり関係なく、名前の変更された引数を使用しても問題にしないことを認める、実用面から見たアプローチです。
offsetGet()のようなメソッドを名前付き引数で呼び出すことは考えにくいですし、継承時に同じ引数名を強制するメリットもありません。

前述のように、幾つかの他言語でもこのアプローチが採用されており、特にPythonは名前付き引数の使用が多い言語です。
このようなアプローチが実際にそれなりにうまく機能しているという実証になるでしょう。

このRFCには含まれていませんが、今後必要性が高くなった場合の代替案がAlternativesセクションにて開設されています。

Internal functions

歴史的に、内部関数には引数のデフォルト値という統一された概念がありませんでした。
どの引数がオプションであるかは外からわかりますが、デフォルト値は実装が仕様であり中を見ないことにはわかりません。

PHP8.0以降、内部関数には外部から取得できるデフォルト値を定義することが可能になりました。
そしてPHPにバンドルされている内部関数では既に対応されています。
このRFCは、そのデフォルト値の情報に基づいています。
スキップされた引数は、関数を呼び出す前にデフォルト値に置き換えられます。

しかし、全ての引数に対してデフォルト値という概念を適用することはできません。
例えば以下のようなものです。

functionarray_keys(array$arg,$search_value=UNKNOWN,bool$strict=false):array{}

array_keys()関数は$search_valueが存在するか否かによって、根本的に異なる動作をします。
$search_valueに渡すことのできるデフォルト値というものはなく、この場合は引数を渡さないのと同じ動作になります。
このような引数はUNKNOWNと表し、引数をスキップすると例外が発生します。

// こっちはOKarray_keys($array,search_value:42,strict:true);// Error: Argument #2 ($search_value) must be passed explicitly, because the default value is not knownarray_keys($array,strict:true);

$search_valueを指定せずに$strictを指定しても意味がないので、これは想定された動作だといえます。

このアプローチの欠点は、動作させるためにデフォルト値の情報を提供しなければならないことです。
まだこの情報を提供していないサードパーティの拡張モジュールは、名前付き引数は動作しますが、引数のスキップはサポートしません。

このRFCの以前のバージョンで行おうとしていた代替案は、未定義値をスタック上に残し、内部パラメータ解析機構(ZPP)によって適切に解釈させることでした。
これは概ね動作しますが、一部のケース、特に明示的にZEND_NUM_ARGS()を使って引数カウントしている場合は、動作を誤ったりクラッシュする可能性もあります。

Documentation / Implementation mismatches

現在、ドキュメントの引数名と実装上の引数名は、常に一致しているとはかぎりません。
このRFCが受理された場合は、この両者の引数を同期させます。
これには、引数名の命名ガイドラインを作成することも含まれます。

Internal APIs

上記のとおり、名前付き引数は内部関数においてはほぼ透過的です。
内部関数は、呼び出しが名前付き引数で行われたかどうかにかかわらず、通常の位置引数で実行されます。
そのため、ほとんどの場合はコードの修正は必要ありません。

特殊なケースとして可変長引数があります。
これは未知の名前付き引数をextra_named_paramsに保存し、ZEND_CALL_HAS_EXTRA_NAMED_PARAMSを立て、execute_dataをコールします。
ほとんどの内部関数は、この情報を使用して有用なことを行うことはできないため、Z_PARAM_VARIADICZ_PARAM_VARIADIC_EXマクロを使う関数は、未知の名前付き引数に遭遇すると自動的にArgumentCountErrorをスローします。

array_merge([1,2],a:[3,4]);// ArgumentCountError: array_merge() does not accept unknown named parameters

未知の名前付き引数を受け入れたい関数は、これらのかわりにZ_PARAM_VARIADIC_WITH_NAMEDマクロを使用する必要があります。

zval*args;uint32_tnum_args,HashTable*extra_named;ZEND_PARSE_PARAMETERS_START(0,-1)Z_PARAM_VARIADIC_WITH_NAMED(args,num_args,extra_named)ZEND_PARSE_PARAMETERS_END();

zend_call_function()は、zend_fcall_info構造体に新しいフィールドを追加することで、名前付き引数をサポートするよう拡張されました。

typedefstruct_zend_fcall_info{/* ... */HashTable*named_params;}zend_fcall_info;

zend_fcall_info構造体をサポートしている初期化関数を使わず手動で初期化する場合は、このフィールドをNULLで初期化する必要があります。

Backwards incompatible changes

狭義には、このRFCにはひとつだけ後方互換性のない変更があります。
call_user_func_array()の引数の文字列キーは、これまでは無視されていましたが、名前付き引数として解釈されるようになります。

それに加え、名前付き引数を想定していないコードで名前付き引数を使用した場合に発生する可能性のある潜在的な問題が2点あります。

ひとつめは、引数名が重要なものとなったので、継承で変更すべきではありません。
そのような変更を行う既存のコードは、実質的に名前付き引数と互換性がないかもしれません。

第二に、可変変数による未知の名前付き引数をうまく扱えないかもしれません。
ほとんどの場合は単に無視されるだけなので無害です。

Alternatives

代替案。
このRFCには含まれません。

To named arguments

私の知っている名前付き引数の実装には2種類の代替案があり、以下に簡単に説明します。

ひとつめは、名前付き引数をオプトインにすることです。
このRFCでは全ての関数・メソッドを名前付き引数で呼び出すことができますが、明示的なオプトインを必要とすることで引数名の変更の問題などを回避することができます。

このアプローチの大きな欠点は、もちろん、名前付き引数が既存のコードで使えないことです。
これはこの機能にとって大きな損失であり、もはや導入する価値がないと考えています。

名前付き引数を望まない機能のために<<NoNamedArgs>>のようなオプトアウトメカニズムを用意する方が、まだ建設的だと思います。
例としてArrayAccessインターフェイスなどがあり、これは直接呼び出されることはなく、実装ごとに引数名が異なるのが普通です。

To parameter name changes during inheritance

このRFCでは、継承時の引数名の変更を黙認します。
これは実用的ですが、引数名を変更されている子オブジェクト上でメソッドを呼び出した際に、エラーが発生する可能性があります。
代替として、親メソッドでの引数の使用も許可する案があります。

interfaceI{publicfunctiontest($foo,$bar);}classCimplementsI{publicfunctiontest($a,$b){}}$obj=newC;// C::test()と合ってる$obj->test(a:"foo",b:"bar");// 動く// I::test()と合ってる$obj->test(foo:"foo",bar:"bar");// こっちもOKにしてしまう

親クラスには引数名foobarに対応したメソッドがあるため、実際にはabとして解釈します。
これにより、このメソッドは疑似的にLSPを満たすようになります。

親メソッドの引数はエイリアスとして登録されますが、特定のシグネチャには縛られません。
従って、異なるシグネチャを混ぜて使うことも可能です。
もちろん推奨はされません。

$obj->test(a:"foo",bar:"bar");// これも動く

設計的には、この呼び出しは禁止した方がよいでしょうが、技術的にも性能的にも対応するコストを書ける意味はなさそうです。

この方式にはひとつ問題があります。
以下はどう解釈すればよいでしょうか。

interfaceI{publicfunctiontest($foo,$bar);}classCimplementsI{publicfunctiontest($bar,$foo){}}

この場合、LSPの継承チェックは単に致命的エラーを出します。
この制限は、実は引数名の変更を禁止するよりも影響が少ないと思われます。
Composerライブラリのトップ2000を調査した結果はhttps://gist.github.com/nikic/6cc9891381a83b8dca5ebdaef1068f4dで見ることができます。

Future Scope

この項目は今後の展望であり、このRFCには含まれていません。

Shorthand syntax for matching parameter and variable name

特にコンストラクタでは、同じ名前のプロパティに引数を代入することが一般的です。

newParamNode(name:$name,type:$type,default:$default,variadic:$variadic,byRef:$byRef);

言語によっては、同じ名前を2回繰り返すことを避けるために特別な構文を提供しているものもあります。
名前付き引数の代替案に応じて、各省略構文がどのようになるかを見てみましょう。

newParamNode(:$name,:$type,:$default,:$variadic,:$byRef);newParamNode(=$name,=$type,=$default,=$variadic,=$byRef);newParamNode(=>$name,=>$type,=>$default,=>$variadic,=>$byRef);

Positional-only and named-only parameters

このRFCの有用な拡張として考えられるものは、位置引数としてしか使用できない引数、または名前付き引数としてしか使用できない引数を定義することです。
これは主にAPI設計者にとって、より自由度が高くなります。
位置引数のみの引数は名前を自由に変更することができ、名前付き引数のみの引数は順番を自由に入れ替えることができます。

Vote

投票は2020/07/24まで、投票の2/3の賛成で受理されます。
2020/07/13時点では賛成35、反対14で賛成が多めです。

というか投票開始直後は賛成9反対8とかで却下圏内だったんですよね。
驚きのあまり普段滅多にしない応援ツイートなんてやってしまいましたよ。

その後は応援ツイートのおかげで持ち直し1ましたが、それでもまだ簡単にひっくり返る程度の票差です。
投票権を持ってる人はYESに投票しよう!

感想

先日Nikitaがプルリクを出してからずっと注目してたRFCなんですよね。

正直満場一致かそれに近い票数になると思ってたので、これだけ反対票が多いのはかなり意外でした。
やはり細かいとはいえ互換性がなかったり動作が微妙だったりするところが気になったりしたのでしょうか。
call_user_func_array()なんて使うんじゃねえ、と言えればいいのでしょうがそうにも行きませんしね。

ということで、おそらくPHP8.0からは名前付き引数が使用可能になるはずです。

何が便利って、呼び出し側で引数の意味がすぐにわかるのが大きいです。
なにしろPHPには引数の順番が異なる関数なんかがたくさんあったりしますからね。
strposは全文字列が1番目、検索対象文字列が2番目なのにpreg_matchは全文字列が2番目で検索対象文字列が1番目だったりとか。
array_*系なんてぐちゃぐちゃですしね。

これが$pos = strpos(heystack:$heystack, needle:$needle); preg_match(subject:$subject, pattern:$pattern, matches:$matches);のようにわかりやすく書けるようになるのは素晴らしいことです。

可変長引数や引数アンパックと組み合わせるとややこしいので、それは単純に避けたほうがよいでしょう。


  1. たぶん1票たりとも影響してない。 


【PHP8.0】PHPにヌル安全オペレータが導入される

$
0
0

ユーザの住んでいる国を取得します。
しかし、うっかりユーザがnullでした。
どうなるでしょう。

$user=null;echo$user->address->country??'';

PHPのプロパティは元よりnull安全なので、存在しないプロパティだろうがnullだろうがプリミティブ型だろうがいきなりプロパティを取り出せます。
取れない場合はE_NOTICEが発生しますが、??を使えば黙殺できます。

しかしメソッドはだめです。

echo$user->getAddress()->getCountry()??'';// Fatal error: Call to a member function getAddress()

律儀にエラーを潰すとこんなかんじになります。

// 1if(method_exists($user,'getAddress')){if(method_exists($user->getAddress(),'getCountry')){echo$user->getAddress()->getCountry();}}// 2if($userinstanceof\User){if($user->getAddress()instanceof\Address){echo$user->getAddress()->getCountry();}}

まあ面倒ですね。

ということでPHPにも流行りのnull安全オペレータが導入されることになりました。

以下は該当のRFC、Nullsafe operatorの日本語訳です。

Nullsafe operator

Introduction

このRFCでは、Null安全オペレータ?->を提案します。

Proposal

メソッドを呼び出したり、計算結果からプロパティを取り出したりといった作業は、nullではない対象にだけ行いたいのが普通です。
現在のPHPではnullチェックを入れる必要があるので、入れ子や繰り返しが多くなります。

$country=null;if($session!==null){$user=$session->user;if($user!==null){$address=$user->getAddress();if($address!==null){$country=$address->country;}}}// $countryが取り出せた

null安全オペレータ?->を使うと、このコードは以下のように書けます。

$country=$session?->user?->getAddress()?->country;

演算子の左側がnullだった場合はチェーン全体の実行が停止され、nullが返ります。
nullでなかった場合は通常の->と全く同じ動作になります。

Short circuiting

Introduction

ショートサーキット、短絡とは、条件に基づいて式の実行をスキップすることです。
よくある例としては&&||などです。

null安全オペレータについて、短絡を実装する方法は3種類ありました。
どの方法がよいか、以下の同じコードスニペットで見ていきましょう。

null?->foo(bar())->baz();

1. Short circuiting for neither method arguments nor chained method calls

・引数もメソッドも全て実行する、短絡しない。

このような例は、今のところHackくらいでしか見られません。
関数bar()もメソッドbaz()も両方とも実行されるため、baz()Call to a member function on nullのエラーが発生します。
3つの選択肢の中で、最も予想外の結果となるでしょう。
前回のRFCでの大きな問題点でした。

2. Short circuiting for method arguments but not chained method calls

・引数は飛ばすけどメソッドは飛ばさない。

これは、通常は短絡しないと呼ばれるものです。
関数bar()は呼び出されませんが、メソッドbaz()は呼び出されてCall to a member function on nullのエラーが発生します。

3. Short circuiting for both method arguments and chained method calls

・引数もメソッドも全てスルー

これは完全短絡と呼ぶことにしましょう。
関数bar()もメソッドbaz()も呼び出されず、エラーは発生しません。

Proposal

このRFCでは、完全短絡を実装します。
チェーン内のひとつの要素の評価に失敗した場合、それ以降の全てが中断され、チェーン全体がnullと評価されます。
以下の要素はチェーンの一部とみなされます。

・配列[]
・プロパティ->
・null安全プロパティ?->
・静的プロパティ::
・メソッドコール->
・null安全メソッドコール?->
・静的メソッドコール::

以下はチェーンに含まれず、別のチェーンが発生します。

・関数の引数
・配列[]内部の式
{}にプロパティアクセスした場合->{}

チェーンは最も短くなるように自動判別されます。
以下はその例です。

$foo=$a?->b();// --------------- chain 1//        -------- chain 2// $aがnullだったらchain2が中断、b()は実行されず$foo=nullになる$a?->b($c->d());// --------------- chain 1//        -------  chain 2// $aがnullだったらchain1が中断、b()は実行されず$foo=nullになる。chain2も実行されない。$a->b($c?->d());// --------------- chain 1//       --------  chain 2// $cがnullだったらchain2が中断、d()は実行されない。chain1の$a->b()は実行されて引数はnullになる。

Rationale

この動作を選択した理由。

1. It avoids surprises

$foo=null;$foo?->bar(expensive_function());

驚きを最小にします。
$fooがnullであればexpensive_function()の実行は望ましくないでしょう。

2. You can see which methods/properties return null

$foo=null;$foo?->bar()->baz();

短絡がないと、チェーン内のメソッド呼び出しやプロパティアクセス全てにnull安全オペレータを使用しなければならなくなります。
不要な部分には使わないことで、どのメソッドやプロパティに問題があるのかを特定することもできます。

3. Mixing with other operators

$foo=null;$baz=$foo?->bar()['baz'];var_dump($baz);// 短絡がない場合:Notice: Trying to access array offset on value of type null// このRFC:null

短絡によって配列アクセス['baz']は完全にスルーされるので、E_NOTICEは発生しません。

Other languages

Stack Overflow 2020 surveyによる人気の高水準言語、および姉妹言語Hackについて、null安全オペレータの実装状況を確認してみます。

言語有無表記短絡
JavaScript?.
Python
Java
C#?.
TypeScript?.
Kotlin?.×
Ruby&.×
Swift?.
Rust
Objective-C※1
Dart?.×
Scala※2
Hack?→※3

※1:nilへのプロパティやメソッド呼び出しは常に無視される
※2:DSL経由で可能
※3:メソッド引数も評価する

13のうち8言語がnull安全オペレータを持っており、そのうち4言語は短絡評価します。

Syntax choice

?が、短絡が発生する正確な場所を表します。
これは、null安全オペレータを実装している他の全ての言語と似ています。

Forbidden usages

Nullsafe operator in write context

null安全オペレータを代入に使用することはできません。

$foo?->bar->baz='baz';// Can't use nullsafe operator in write contextforeach([1,2,3]as$foo?->bar->baz){}// Can't use nullsafe operator in write contextunset($foo?->bar->baz);// Can't use nullsafe operator in write context[$foo?->bar->baz]='baz';// Assignments can only happen to writable values

このRFCの以前のバージョンでは、=の左側にnull安全オペレータを使った場合、nullであれば代入をスキップすることが提案されていました。
しかし、技術的な問題からこの仕様は外されました。
今後のRFCで追加されるかもしれません。

References

リファレンスは許可されません。
参照するためには変数やプロパティのメモリ上の値が必要となりますが、null安全オペレータはnullを返すことがあるからです。

$x=&$foo?->bar;// おおむね↓と同じif($foo!==null){$x=&$foo->bar;}else{$x=&null;// Only variables should be assigned by referenceのエラー}

従って、以下のような例は禁止となります。

$x=&$foo?->bar;// Compiler error: Cannot take reference of a nullsafe chaintakes_ref($foo?->bar);// Error: Cannot pass parameter 1 by referencefunction&return_by_ref($foo){return$foo?->bar;// Compiler error: Cannot take reference of a nullsafe chain}

引数に参照を渡せるかどうかはコンパイル時にはわからないため、2番目の例は実行時エラーになります。

Backward Incompatible Changes

既知の後方互換性のない変更はありません。

Future Scope

PHP7.4以降、nullに配列アクセスnull["foo"]するとE_NOTICEが発生します。
そのため、演算子?[]も許可し、$foo?["foo"]と書けるようになると有用かもしれません。
しかし三項演算子$foo?["foo"]:["bar"]と曖昧になってしまうため、このRFCには配列へのnull安全オペレータは含まれていません。

投票

期間は2020/07/31まで、投票者の2/3の賛成で受理されます。
2020/07/20時点では賛成44反対2で、まず確実に導入されます。

感想

正直null安全があまり理解できてないので、このRFCがどのくらい有用なのかもよくわかっていません。
個人的には、メソッドチェーンが書きやすくなるくらいしかメリットを感じられていないです。
そもそもPHPでは元々型がアバウトなうえ、うっかり下手にぬるぽが起こってたとしてもメモリを壊したりなんてことはまずできませんからね。
最悪でもせいぜい500 Internal Server Errorになる程度です。

しかし投票を見るに、union型str_containsを遥かに超え、JITmatch式に匹敵するほどの圧倒的賛成多数です。
それだけ有用であると皆に判断されたということで間違いないでしょう。
PHPにおけるnull安全の優秀さはきっと教えてくれるはず。

あなたのCSS力の助けになってくれる素晴らしいWebサイト12選

$
0
0

以下はAman Varma( Twitter / GitHub / stackoverflow / Webサイト )による記事、12 Super websites to help you with your CSS problems ☺の日本語訳です。

12 Super websites to help you with your CSS problems ☺

これらは、あなたのCSSをより楽しく簡単に装飾してくれる、そしてもしかしたらあなたが躓いているCSSの問題の多くを解決してくれるかもしれないWebサイトたちです。

1. Pixel art

01.png

あなたもきっとピクセルアートが好きに違いありません。
90%の人はpngでピクセルアートを書いてサイトに追加していると思いますが、でもそのピクセルアートをCSSで書けたらもっといいと思いませんか?
そこでPixel Art to CSSです。
書いたピクセルアートをCSSに変換してくれます。

Pixel Art to CSS

2. Gradient

02.jpg

CSSグラデーションは非常に美しいですが、使いこなすのは難しいものです。
正しい色を見つける必要があり、何行ものコードを書かなければなりません。
また互換性に気を使うと、さらに多くのコードが必要になります。
それらの問題を解決するためにCSS GradientのWebサイトが存在します。
直感的なカラーセレクタや後方互換は大きな助けになってくれるでしょう。
ここにはまた、Gradient BackgroundsColor Shadesといったツールも存在します。

CSS Gradient

3. Compatibility

03.jpg

古いブラウザには対応していないCSSタグや、逆に新しすぎて誰も対応していないようなCSSタグがたくさん存在します。
自分の使っているタグがどれだけのブラウザに対応しているかは、様々なサイトを見て確認しなければなりません。
そんなときに役立つのがCanIUseです。
CanIUseは、どのブラウザがどのタグをサポートしているかを教えてくれます。
またNewsセクションでは、ブラウザが新しく対応したタグの状況について教えてくれます。

CanIUse

4. Cheatsheets

04.png

もしかしたら、私のようにCSSタグをよく忘れてしまうかもしれません。
そんなときのために、手元にCSSチートシートを用意しています。
CSSチートシートで検索すると数千もの結果が出てくるのですが、その中でも最高のものであると私が信じているもののひとつがDevhintsです。
CSS以外にも、HTML、Python、Rubyなど、多くの言語のチートシートが用意されています。

Devhints

5. Color Palettes

05.png

あなたのプロジェクトにぴったりのイメージカラーは見つかりましたか?
ネットでは多くの素晴らしいオンラインカラーパレットを見出すことができますが、私が気に入っているのはシンプルなColor Huntです。
新着順、注目順、ランダム順などで並べ替えることができ、最も頭を使わずにカラーパレットを選択することができます。

Color Hunt

6. Unicode

06.png

世界には何万ものUnicodeが存在するので、全てを覚えきれない人もときにはいることでしょう。
この問題を解決するためにUnicode Tableが存在します。
あなたのプロジェクトにマッチする絵文字やUnicodeを、Unicode Tableから探してみましょう。
また、 𓂸のような面白Unicodeも見つけられるかもしれません。

Unicode Table

7. CSS Validator

07.png

CSSを正しく使えているか、どのように判断しましょう。
W3C CSS Validatorを使えばいいのです。
W3C CSS Validatorは、WebデザイナーやWeb開発者が作ったCSSをチェックできるように、W3Cが制作したサイトです。

CSS Validator

8. Old Browser support

08.png

あなたのWebサイトを、未対応のユーザエージェントを持つ古い古いブラウザや、新しいブラウザでも見れるようにしたいと思いませんか?
そこでAutoprefixerです。
AutoprefixerはCSSを解析し、Can I Useの対応状況を見て、CSSにベンダープレフィックスを追加してくれます。
AutoprefixerはGoogleにも推奨されていて、実際にTwitterやTaobaoが使用しています。

Autoprefixer CSS online

9. Other awesome tools

09.jpg

Bennett Feelyは、初心者にもプロにも助けになるような、素晴らしいWebサイトを幾つも開発しています。
私が最も気に入っているのはCSS Pie Chartで、conic-gradientを使った円グラフを生成することができます。
またClippyはCSSで画像をクリッピングし、Image Effect wit CSSではCSS blend modeを使った様々な画像効果を紹介し、そしてCSS Gradientsでは様々なCSSグラデーションを参照することができます。

Bennettfeely

10. Tobias Ahlin Thanks to Siddhartha Sarkar

10.png

Tobiasは、Webサイトで幾つもの素晴らしいCSSを公開しているもう一人の開発者です。
彼のWebサイトからProjectセクションを見てみると、彼が手掛けた多くの作品、Moving LettersTypeSourceSpinKitなどを見ることができます。
とても役に立ちそうなCSSです。

Tobias Ahlin Bjerrome

11. Hayk An:: Workbench Thanks to Amruth Pillai

11.png

Hayk AnのWebサイトには、インターネット上で最も有用で興奮させられるサイトのいくつかを見つけることができます。
これらはインスピレーションの金脈であり、あなたのウェブサイトデザインにきっと多くの刺激を与えてくれるでしょう。

Hihayk

12. You can suggest in the comment I will make it my number 12 or we can till 20 ;)

あなたの好きを12番目にしてくれていいのよ。

12.gif

ピクセルアート By lipixelart

コメント欄

Spinkit.cssがかっこいいローディングしてくれる。」「この作者のサイトを追加したよ。」
「アプリを作るときにScaleでカラーパレットを選択したよ。」「この作者のサイトを追加したよ。」
「CSSセレクタがわからないときのSelectors Explainedいいぞ。」
「有益なリンクありがとう」
「いくつかお気に入りに登録した!」
「素晴らしいキュレーションありがとう😍 」

感想

もはや今のCSSは、私のような一般人のレベルでは何も作れないところにまでなっています。
一からどうにかするのは諦めて、誰かが作ってくれている、これら優れたツールやパーツを拝借させてもらって、ブラッシュアップを図っていきましょう。
単にコピペするだけでもワンランク上の見た目を作れるようになる、素晴らしいサイトたちです。

まあ私はフロントエンドエンジニアではないので、最近はもうBulma突っ込んで適当にdiv生やして終わりますけどね。

【PHP8.0】非厳密な比較演算子`==`の挙動が今さら変更になる

$
0
0

推移律?
そんなものはこの世の果てに置いてきた。

PHP7
"true"==0;0=="0";"true"=="0";

結果は順にtrue、true、falseです。

これがPHP7までの非厳密な比較(等価)演算子だったわけですが、まあおかしいよねってことで、この挙動がPHP8.0で変更になることになりました。
よもや今さら基本中の基本である比較演算子の動作を弄ってくるとは思わなかったぞ。

以下はSaner string to number comparisonsの日本語訳です。

PHP RFC: Saner string to number comparisons

Introduction

==やその他の非厳密な比較演算子を用いた文字列と数値の比較は、現在は、文字列を数値にキャストし、その後整数か浮動小数の比較を行っています。
この結果、多数の不可解な結果が得られますが、中でも注目すべきは0 == "foobar"がtrueになることです。
このRFCでは、文字列が実際に数値型文字列である場合にのみ数値型で比較を行うことにすることで、非厳密な比較をより直感的にし、問題になる動作が起こりにくくすることを提案します。
そうでない場合は、数値を文字列型に変換し、文字列比較を行うことにします。

PHPでは大別して2種類の比較演算子をサポートしています。
厳密な比較===および!==と、非厳密な比較==!=>>=<<=<=>です。
両者の主な違いは、厳密な比較は両方のオペランドが同じ型であることが前提で、暗黙の型変換を行わないことです。
しかし、それ以外にもいくつか異なる点が存在します。

・厳密な比較は、strcmp()で比較を行うが、非厳密な比較は、数値型文字列である場合は数値に変換する"スマートな"比較を行う。
・厳密な比較は、配列の順番も同じ必要があるが、非厳密な比較は、配列の順番が異なっていてもよい。
・厳密な比較は、オブジェクトが同一かをチェックするが、非厳密な比較は、オブジェクトの値を比較する。

非厳密な比較は常に避けるべきである、というのが現在のPHPでのドグマになっています。
バグの最大の原因は0 == "foobar"がtrueになるという事実でしょう。
これはin_array()switchなど、比較が暗黙のうちに行われている場合によく発生します。

$validValues=["foo","bar","baz"];$value=0;var_dump(in_array($value,$validValues));// true ←

これは不幸なことです。
なぜならば、PHPのような言語では、非厳密な比較に価値がないということは決してないからです。
42"42"を同じ値であると見なすことは多くの場合において有用です。
さらにPHPでは言語側が暗黙のうちに変換を行うことがあります。
たとえば配列のキーが整数型文字列の場合は整数に変換されます。
さらに、switchのような幾つかの構文は非厳密な比較しかサポートしていません。

非厳密な比較の考え方には多くのメリットが存在しますが、しかし残念なことに現在の比較のセマンティクスは明らかに間違っており、非厳密比較全体の有用性を大きく落としてしまっています。

このRFCでは、文字列と数値の比較をより合理的にすることを意図しています。
数値文字列を数値と比較する際には、文字列を数値に変換してから比較し、これは現在と同じ動作です。
それ以外の場合は、数値を文字列に変換して文字列比較を行います。
このRFCによって、幾つかの単純比較がどのように変更されるかを次の表に示します。

ComparisonBeforeAfter
0 == "0"truetrue
0 == "0.0"truetrue
0 == "foo"truefalse
0 == ""truefalse
42 == " 42"truetrue
42 == "42foo"truefalse

この仕組みを理解するためには、数値型文字列の比較を見てみるとよいでしょう。
上の表と、下の数値型文字列と文字列の比較の結果表を見比べてみましょう。
(下の表は、このRFCでは変更されません)

ComparisonResult
"0" == "0"true
"0" == "0.0"true
"0" == "foo"false
"0" == ""false
"42" == " 42"true
"42" == "42foo"false

上記の説明は簡略化されたものです。
詳細な仕様は後述しますが、これだけで新しい仕様の直感的な動作と、どうしてこの仕様を選択したのかはわかると思います。

Proposal

このRFCは、以下の演算子と関数、およびこれ以外にも非厳密な比較を行う全ての操作に適用されます。

・演算子==!=>>=<<=<=>
・関数in_array()array_search()array_keys()strict=trueを渡さない場合
・ソート関数sort()rsort()asort()arsort()、`array_multisort()にSORT_REGULARを渡した場合

このRFCの正確な定義は以下の通りです。

$int <=> $string
 ・ $stringが正規の整数型文字列である場合、$int <=> (int)$string
 ・ $stringが正規の浮動小数型文字列である場合、(float)$int <=> (float)$string
 ・ それ以外の場合、strcmp((string)$int, $string)を1/0/-1に正規化した値

$string <=> $int
 ・ return -($int <=> $string)

$float <=> $string
 ・ $floatがNANであれば1
 ・ $stringが正規の整数型文字列である場合、$float <=> (float)$string
 ・ $stringが正規の浮動小数型文字列である場合、$float <=> (float)$string
 ・ それ以外の場合、strcmp((string)$int, $string)を1/0/-1に正規化した値

$string <=> $float
$floatがNANであれば1
return -($float <=> $string)

ここには幾つかの微妙な要素が要素が絡んでいます。

Well-formed numeric strings

正確な定義は言語仕様書に書かれていますが、正規の整数型文字列とは、オプションの空白の後に10進整数もしくは浮動小数リテラルが続くものであると簡潔に記載されています。
正規でない整数型文字列とは、正規の整数型文字列の末尾に追加の文字列があるものです。
それ以外の全ての文字列は、整数型文字列ではありません。

このRFCでは、正規の整数型文字列についての比較はこれまでと全く同じです。
これは42 == "42"のような単純なケースだけではなく、数値が異なる形式で与えられた場合でも同様です。

// 前後で変更なしvar_dump(42=="000042");// truevar_dump(42=="42.0");// truevar_dump(42.0=="+42.0E0");// truevar_dump(0=="0e214987142012");// true

文字列による非厳密な比較においても結果は全く同じです。

// 前後で変更なしvar_dump("42"=="000042");// truevar_dump("42"=="42.0");// truevar_dump("42.0"=="+42.0E0");// truevar_dump("0"=="0e214987142012");// true

このRFCによる差異は、正規でない数値文字列、もしくは数値型でない文字列の場合にのみ発生します。

// Before | After | Typevar_dump(42=="   42");// true   | true  | 正規var_dump(42=="42   ");// true   | false | 非正規var_dump(42=="42abc");// true   | false | 非正規var_dump(42=="abc42");// false  | false | 非数var_dump(0=="abc42");// true   | false | 非数

目を引くのは、" 42"と"42 "において結果が異なるところです。
この矛盾は、別途Saner numeric stringsのRFCにおいて解消されます。

Precision

比較方法を単純に定義するのではなく、数値を文字列にキャストして非厳密な比較を行うという回りくどい方法を採用しているのは、PHPによる浮動小数から文字列への変換はini設定precisionの影響を受けるからです。

正規の浮動小数文字列との比較は、この設定に無関係に処理されます。
しかし、非正規の浮動小数文字列との比較では、以下のように影響してきます。

$float=1.75;ini_set('precision',14);// 小数点以下14桁、デフォルトvar_dump($float<"1.75abc");// ↑↓だいたい同じvar_dump("1.75"<"1.75abc");// trueini_set('precision',0);// 小数点以下0桁var_dump($float<"1.75abc");// ↑↓だいたい同じvar_dump("2"<"1.75abc");// false

Special values

浮動小数には、幾つかの特殊な非数値が存在します。

// Before | Aftervar_dump(INF=="INF");// false  | truevar_dump(-INF=="-INF");// false  | truevar_dump(NAN=="NAN");// false  | falsevar_dump(INF=="1e1000");// true   | truevar_dump(-INF=="-1e1000");// true   | true

注目すべきが2点あります。
まず、INF"INF"と等しくなるようになりました。

ただし、NAN"NAN"と等しくなりません。
NANはあらゆる比較演算子にfalseを返し、<=>NANがどちらにあるかに関わらず1を返します。
これは、値が比較不可能であることを示すPHP内部の方法です。

NANの特別なセマンティクスはIEEE-754に従っており、NANを含む比較は常にfalseになります。

Backward Incompatible Changes

非厳密な比較のこの変更は、後方互換性がありません。
さらに悪いことに、このRFCはPHPコア機能を暗黙のうちに変更することになります。
PHP7.4ではある動作を行っていたコードが、PHP8.0では異なる動作を行うことになります。
そして影響を受けるケースを検出するために静的解析を使用すると、多くの誤検出が発生する可能性があります。

しかし比較の変更について調査を行ったところでは、この変更による実際の影響は、想像していたよりずっと小さいことがわかりました。
ただし、これはテスト対象のコードベースに大きく依存します。

投票

期間は2020/07/31まで、投票者の2/3の賛成で受理されます。
本RFCは賛成44反対1の圧倒的賛成多数で受理されました。

感想

変更後の動作を見るてみと、確かにこっちの方が元の動作よりも妥当っぽいよね、とは思うのですが、だからといって今さら==の挙動に手を加えてくるとはさすがに思いませんでしたよ。
なかなか思いきったことをする。

この変更によって自然と

switch('foo'){case0:'$fooは0だよ';}

switchでよく問題となっていたこの挙動が発生しなくなります。
ということはつまりmatch式いらないのでは?と一瞬思わないでもないですが、switchはそれ以外にも色々アレなのでやっぱりあったほうがいいですね。

さて、この変更により、きっと予想より多くのコードに影響が出ることでしょう。
RFC中では影響は意外と少ないと言っていましたが、それはあくまで少なくともGitHubに上げることのできる程度の能力がある者が書いたコードだからであり、最低限の水準は維持されているからです。

そう、GitHubに上げる能力すらない者たちによって書かれた絶望と混沌が世の中には溢れているのですよ。

と思ったけど、そんなコードはそもそもPHP8では動かないだろうし、そしてバックエンドで動いているだろうPHP5が8にアップグレードされるなんてこともないだろうから、別に問題ないか。
それに動作が変更になるといっても、基本的には想定したように動くようになる変更ですしね。
即ち、現実的なソースコードにはほとんど影響ありません。

【PHP8.0】PHP8.0の新機能

$
0
0

PHP8.0 / PHP7.4

2020/08/04にPHP8.0がフィーチャーフリーズしました。
言語機能に関わるような機能の追加・変更が締め切られたということです。
今後はデバッグを繰り返しながら完成度を高めていき、2020/12/03にPHP8.0がリリースされる予定です。

というわけでPHP8.0で対応することが決まったRFCを見てみましょう。

RFC

JIT

賛成50反対2で受理。
PHP8の目玉、JITです。

PHPをネイティブコードにコンパイルし、さらにコンパイルした結果を次のリクエストに使い回すことができます。
平均的に1.3-1.5倍程度、さらにCPUバウンドな処理なら3倍以上という劇的な高速化が見込めます。

PHP7.4で入ったプリローディングと組み合わせれば、これってもうコンパイル言語なのでは?

Named Arguments

賛成57反対18で受理。
名前付き引数です。

// これまでarray_fill(0,100,50);// 名前付き引数array_fill(start_index:0,num:100,value:50);// 同じhtmlspecialchars($string,ENT_COMPAT|ENT_HTML401,'UTF-8',false);htmlspecialchars($string,double_encode:false);

Pythonのやつとだいたい一緒です。

引数の多い関数で最後の引数だけ指定したいといった場合に有用です。
また、これまでは引数の順番を知らないとどの引数が何を表しているのかわかりませんでしたが、引数名で書くことで読みやすくなります。

Match expression v2

賛成43反対2で受理。
match式です。

echomatch("1"){true=>'Foo',1=>'Bar',"1"=>'Baz',};// Baz

厳密な比較、フォールスルーしない、返り値を持つ式である、といった具合に既存のswitch文の欠点のほとんどを解消したナイスな構文です。
逆に分岐内部には1式しか書けないため、分岐内部での複雑な処理はできません。
今後はswitchを使わざるを得ないところ以外はmatchで書くとよいでしょう。

Nullsafe operator

賛成56反対2で受理。
ヌル安全オペレータです。

$country=$session?->user?->getAddress()?->country;

途中にnullが入ってくる可能性があるメソッドチェーンを簡単に書けるようになります。
?に引っかかった時点で実行が中断され、それより先は処理されません。

// bar()、baz()は実行されないnull?->foo(bar())->baz();

Union Types v2

賛成61反対5で受理。
UNION型のRFCです。

functionsetNumber(int|float$number):void{// $numberはintかfloat}

複数の型を受け取る、もしくは返すことができるようになります。
やりすぎると型システムの意味がなくなるので、程々に使っていくとよいでしょう。

Mixed type v2

賛成50反対11で受理。
なんでもあり型です。

classA{publicfunctionfoo(mixed$value):mixed{return$value;}}

var_dump()の引数のように、あらゆる型を受け取りたい場合に使用する型です。
TypeScriptでいうところのanyです。
自発的に使用するのは基本的にやめておいたほうがよいでしょう。

Attributes (v2)

賛成51反対1で受理。
アトリビュートです。

<<>>でメタデータを埋め込むことができるようになります。

<<WithoutArgument>><<SingleArgument(0)>><<FewArguments('Hello','World')>>functionfoo(){}

これまではPHPDocPSR-5のようにコメントで書くしかなく、強制力もありませんでした。
アトリビュートは、PHPの構文として実効力のある方法でメタデータを書けるようになります。

Attribute Amendments

複数件の投票がありますが、全て受理されています。
アトリビュートの細かな追加仕様のRFCです。

アトリビュートをカンマ区切りで複数書けるようにする、PhpAttributeだったクラス名をAttributeにする、などの仕様追加です。
アトリビュート自体の大きな変更はないようです。

Shorter Attribute Syntax

アトリビュートの構文変更のRFCです。

アトリビュートの構文には幾つか問題があります。

<<Bar(2*(3+3)>>Baz,(4+5)*2)>><<JoinTable("User_Group",<<JoinColumn("User_id","id")>>,<<JoinColumn("Group_id","id")>>,)>>

ネストしたりビット演算子と混ざった場合に非常にわかりにくい、あと今後ジェネリクスが追加されたときに更に混乱する、などです。
ということで代替構文が@@#[]<<>>の間で争われ、投票でもRedditでも@@が最多数でした。

@@JoinTable("User_Group",@@JoinColumn("User_id","id"),@@JoinColumn("Group_id","id"),)

ただ決定はしたものの拒否反応を持つ人も多く、あと@@構文にも後から問題が見つかったみたいな話をしてるっぽいので、もしかしたら特例で変更があるかもしれません。

Reclassifying engine warnings

賛成54反対3で受理。
警告レベルが厳しくなります

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

こんな訳のわからないコードでもPHP7ではE_WARNING止まりで動いていたのですが、今後はErrorExceptionになります。
あまり正しくない20以上の書式について、これまでE_NOTICEだった警告の一部がE_WARNINGに、E_WARNINGだった警告の一部が例外にと、厳しくなる方向に変更されます。

Stricter type checks for arithmetic/bitwise operators

賛成57反対0で受理。
プリミティブでない値の算術演算のRFCです。

PHP7
var_dump([]%[42]);var_dump(newstdClass>>tmpfile());

全く意味の分からない演算ですが、PHP7ではとりあえず動きます。

が、こんなものが動いたところで役には立たないため、PHP8.0以降、配列・リソース・オブジェクトへの算術演算は全てTypeErrorになります。

オブジェクトの場合、演算子オーバーロードが設定されている一部のクラス(GMPなど)は引き続き演算が可能です。
またstringやintなどのプリミティブ型については、このRFCでの影響はありません。

Consistent type errors for internal functions

賛成50反対2で受理。
内部関数の型引数についてのRFCです。

functionfoo(int$bar){}foo("not an int");// TypeErrorstrlen(newstdClass);// Warning: strlen() expects parameter 1 to be string, object givenvar_dump(DateTime::createFromFormat(newstdClass,"foobar"));// E_WARNINGvar_dump(DateTime::createFromFormat("foobar","foobar",newstdClass));// TypeError

正しくない型の引数を渡した場合、ユーザ定義関数では常にTypeErrorが発生します。
内部関数の場合、TypeErrorだったりE_WARNINGだったりします。

これはよくないのでTypeErrorに統一します。

従って、昔ながらのE_WARNINGを抑えるようなコードを書いていた場合はここで詰まります。

PHP本体のテストコードにも、この変更に引っかかるコード(わざとエラーを出すテスト)が1500ほどあるみたいです。

Saner string to number comparison

賛成44反対1で受理。
非厳密な比較演算子==の挙動を変更するRFCです。

"true"==0;// true  PHP7まで"true"==0;// false PHP8

数字と数値型でない文字列を比較する場合、これまでは文字列を数値に変換してから比較していましたが、今後は数値を文字列に変換してから比較するようになります。
数値と数値型文字列の比較や、数値以外の比較はこれまでと同じです。

一見非常に危なそうな変更ですが、この変更で挙動がおかしくなる品質のプログラムは、まず間違いなくその前にReclassifying engine warningsConsistent type errors for internal functionsStricter type checks for arithmetic/bitwise operatorsあたりに引っかかって動かなくなるので、実害はほぼ無いと思います。

Saner numeric strings

賛成30反対4で受理。
数値型文字列の定義を変更するRFCです。

PHP7までは、数値型文字列の定義が複数ありました。

・正規の数値型文字列:0個以上のスペース+数値
・非正規の数値型文字列:正規の数値型文字列+任意の文字列
・それ以外は全て数値型文字列ではない。

functionfoo(int$i){var_dump($i);}foo("123");// int(123)foo("   123");// int(123)foo("123   ");// int(123) with E_NOTICE "A non well formed numeric value encountered"foo("123abc");// int(123) with E_NOTICE "A non well formed numeric value encountered"foo("string");// TypeError

このせいで色々と面倒なことになっていました。
特に 123123 が異なるのはとても違和感がありますね。

ということで、PHP8では数値型文字列の定義をひとつにします。

・数値型文字列:0個以上のスペース+数値+0個以上のスペース
・それ以外は全て数値型文字列ではない。

これにより 123123 は緩やかに同じ値になります。
そして、123abcは数値型文字列ではなくなります。

functionfoo(int$i){var_dump($i);}foo("123");// int(123)foo("   123");// int(123)foo("123   ");// int(123)foo("123abc");// TypeErrorfoo("string");// TypeError

ぶっちゃけSaner string to number comparisonなんかより、こちらのほうがよっぽど影響範囲が大きいです。
ただ数値型文字列ではなくなったとはいえ、(int)"123abc"0になるとあまりにも被害甚大すぎるので、さすがにこれは123にしてくれるようです。

Treat namespaced names as single token

賛成38反対4で受理。
namespaceのパーサトークンを変更するRFCです。

これまで名前空間Foo\Barは字句解析でT_STRING,T_NS_SEPARATOR,T_STRINGと分解されていましたが、今後はT_NAME_QUALIFIEDひとつになります。

PHPを使う側としては、token_get_allを使っているような変態スクリプト以外は全く無関係です。
と思いきやしれっと、名前空間の空白禁止されるとか書いてありました。
ちなみに違反するスクリプトは、Composer上位2000パッケージ中僅か5か所だけでです。

Allow trailing comma in parameter list

賛成58反対1で受理。
関数の引数の末尾カンマのRFCです。

PHPでは配列の末尾カンマは昔から使えていて、その後PHP7.3で関数呼び出しおよびほとんどのリストでも末尾カンマが使えるようになりました。

関数呼び出し側は対応したのに、関数の引数のほうは何故か対応していなかったので、その対応の追加です。

functionhoge($foo,$bar,// PHP8.0以降OK){}hoge(1,2,);// PHP7.3以降OK

Allow trailing comma in closure use lists

賛成49反対0で受理。
use内の末尾カンマのRFCです。

$longArgs_longVars=function($longArgument,$longerArgument,$muchLongerArgument,// ↑の引数末尾カンマでOKになった)use($longVar1,$longerVar2,$muchLongerVar3,// このRFC){};

useの末尾にもカンマを置くことができなかったので、その対応の追加です。
この抜けの補完によって、おそらく全ての列挙箇所で末尾カンマを使えるようになったのではないでしょうか。

Ensure correct signatures of magic methods

賛成45反対2で受理。
マジックメソッドの引数のRFCです。

マジックメソッドには、これまでは正しくない型を書くことが可能でした。

PHP7までOK
classFoo{publicfunction__get(array$name):void{}}

今後はこれが禁止され、正しい型しか書けないようになります。
もしくは、単に何も書かないかです。

PHP8.0
classFoo{publicfunction__get(string$name):mixed{}// ↓でもOKpublicfunction__get($name){}}

例によってNikitaがComposer上位1000パッケージを調べたところ、違反するスクリプトは僅か7か所だけでした。

Configurable string length in getTraceAsString()

賛成36反対2で受理。
例外の文字列展開の文字数を変更するRFCです。

Throwable::getTraceAsString()などでエラーを文字列展開する際、スタックトレース中の文字列は15バイトで打ち切られてしまいます。
そのため、深く調査したい場合などに難しい状態でした。

そこでini設定zend.exception_string_param_max_lenを追加し、設定されたバイト数をスタックトレースに展開できるようにします。
デフォルトは既存の15です。

Remove inappropriate inheritance signature checks on private methods

賛成24反対11で受理。
privateメソッドの継承に関するRFCです。

classA{finalprivatefunctionfinalPrivate(){echo__METHOD__.PHP_EOL;}}classBextendsA{privatefunctionfinalPrivate(){echo__METHOD__.PHP_EOL;}}

privateメソッドは継承されないにも関わらず、この書き方は何故かCannot override final methodのFatal Errorになります。
その他幾つかの場合において、継承時にprivateメソッドのオーバーライドチェックが行われることがあります。

従って、PHP8.0以降privateメソッドは継承時にオーバーライドチェックを行わないようにします。

Abstract trait method validation

賛成52反対0で受理。
トレイトのabstractメソッドの挙動に関するRFCです。

トレイトにabstractメソッドを書いた場合、継承時のルールが何かおかしくなっています。

PHP7までOK
traitMyTrait{abstractpublicfunctionpublicFunction():string;abstractprivatefunctionprivateFunction():string;// cannot be declared private}classTraitUser{useMyTrait;// 何故かOKpublicfunctionpublicFunction():stdClass{}}

何故かシグネチャが異なっていても通ってしまうので、これを通常のクラス継承と同じように修正します。
またついでにabstract privateメソッドを定義できるようにします。

PHP8.0
traitMyTrait{abstractprivatefunctionneededByTheTrait():string;publicfunctiondoSomething(){returnstrlen($this->neededByTheTrait());}}classTraitUser{useMyTrait;// OKprivatefunctionneededByTheTrait():string{}// 返り値の型が異なるのでNGprivatefunctionneededByTheTrait():stdClass{}// staticとnonstaticなのでNGprivatestaticfunctionneededByTheTrait():string{}}

traitのabstract privateメソッドは、useしたクラスで実装が必須となります。
privateメソッドの実装を強制させるような状況って、あまり考えたくありませんが。

Make sorting stable

賛成24反対11で受理。
ソートを安定ソートにするRFCです。

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

PHPのソートはこれまで不安定ソートだったので、ソート後にabの順番、cdの順番が保証されませんでした。
PHP8.0以降はソート後の順番が['a' => 0, 'b' => 0, 'c' => 1, 'd' => 1]で保証されます。

Constructor Property Promotion

賛成46反対10で受理。
オブジェクト初期化子です。

classPoint{// プロパティx,y,zが生えるpublicfunction__construct(publicfloat$x=0.0,publicfloat$y=0.0,publicfloat$z=0.0,){}}

コンストラクタにかぎり、引数に可視性を指定すると自動的にプロパティとして登録してくれます。
これまではオブジェクトの初期化に多数の定型文が必要でしたが、今後は楽に書けるようになります。

Always available JSON extension

賛成56反対0で受理。
JSONを常時有効にするRFCです。

実はこれまでJSONエクステンション--disable-jsonオプションでインストールしないようにできていたのですが、これができなくなります。

マニュアルにも書かれていないほどの隠し機能なので、無くなっても全く問題ないでしょう。

Unbundle ext/xmlrpc

賛成50反対0で受理。
ext/xmlrpcを外すRFCです。

xmlrpcは内部的にxmlrpc-epiを使っていますが、こちらが既に放棄されているためサポートから外します。
これによってxmlrpc_xxx関数が使えなくなります。

元より実験的なものである警告がなされていたモジュールなので、これが影響する人はほとんどいないでしょう。

Non-capturing catches

賛成48反対1で受理。
例外を受け取らないRFCです。

try{changeImportantData();}catch(PermissionException){echo"You don't have permission to do this";}

catchした例外を使用しない場合は、最初から受け取らないようにすることができます。
決して例外の握り潰しに使うための機能ではありません。

Locale-independent float to string cast

賛成42反対1で受理。
一部言語での(string)キャストに関するRFCです。

フランス語では小数点が,です。
このためロケールをフランス語に設定した場合、(string)3.14"3,14"になります。

setlocale(LC_ALL,"de_DE");$f=3.14;// float(3,14)$s=(string)$f;// string(4) "3,14"$f=(float)$s;// float(3)

しかし"3,14"を小数に戻すことはできず3になります。
この矛盾を解消するため、またそもそも文字列表現が異なる値になる時点でわかりにくいので、stringキャストはロケールに関わらず常に"3.14"と変換することにします。

英語・日本語など小数点が.のロケールには全く影響ありません。

Change default PDO error mode

賛成49反対2で受理。
PDOのデフォルトエラーモードのRFCです。

PHP7までPDO::ATTR_ERRMODEのデフォルトはPDO::ERRMODE_SILENTでした。
これはエラーが出ても何も言わないので非常によろしくない設定です。

PHP8.0からはPDO::ERRMODE_EXCEPTIONがデフォルトになります。
めでたし。

Add str_starts_with and str_ends_with to PHP

賛成51反対4で受理。
StartsWith/EndsWithです。

echostr_starts_with('abcdef','abc');echostr_ends_with('abcdef','def');

ユーザランドで容易く実装できそうに見えますが、実はこの関数の実装には罠が多く、下手に書くとすぐに非効率になります。
言語側で用意してくれるならそれにこしたことはないでしょう。

str_contains

賛成43反対9で受理。
str_containsです。

str_contains('放課後アトリエといろ','放課後');// true

何故かこれまでずっと存在せず、strpos($haystack, $needle)!==falseという謎の書式を強いられていた『〇〇を含む』が、ようやく簡潔に書けるようになります。

str_containsとstr_starts_with/str_ends_withが実装されたことにより、PHPのテキスト処理に対する関数は出揃ったと言えそうです。

throw expression

賛成46反対3で受理。
throw文がthrow式になります

// PHP7if(!$nullableValue){thrownewInvalidArgumentException();}$value=$nullableValue;// 概ね同じ$value=$nullableValue??thrownewInvalidArgumentException();

throwはこれまで文だったので、式の間に含めることができませんでした。
今後はもっと気軽にthrowしていくことができるようになります。

Object-based token_get_all() alternative

賛成47反対0で受理。
token_get_allの代替構文のRFCです。

token_get_all関数は、パースした結果を配列で返してきていました。

このRFCでは新たにPhpTokenクラスを導入し、パースした結果をオブジェクトで受け取るようにします。
これによってコードがわかりやすくなり、メモリ消費量も減少します。

互換性のため、既存のtoken_get_allはそのままです。

Stringable

賛成29反対9で受理。
StringableインターフェイスのRFCです。

Countableインターフェイスを実装するとcountできるようになり、Traversableインターフェイスを実装するとforeachできるようになります。

同様に__toStringできるようになるインターフェイスStringableを実装しようという提案です。

interfaceStringable{publicfunction__toString():string;}

これを強制するとありとあらゆる実装がぶち壊れるため、__toStringを実装しているクラスは暗黙的にStringableをimplementsします。
従って、使用する側としては特に気にする必要はありません。

どちらかというとライブラリでstring|Stringableを受け取りたいときのためのマーカーインターフェースのようなものです。
実際のユースケースはSymfonyで見ることができます。

Allow ::class on objects

賛成60反対0で受理。
クラス名を取得するRFCです。

stdClass::classでクラス名"stdClass"を取得できますが、いったんオブジェクト化してしまうと何故か::classできません。
get_classを使う必要があります。

PHP8では$object::classと書けるようになります。

echostdClass::class;// classecho(newstdClass)::class;// PHP8.0以降OK

Static return type

賛成54反対0で受理。
返り値にstaticを書けるようにするRFCです。

PHP7では、返り値としてparentselfを書くことが可能です。

<?phpclassFUGA{}classHOGEextendsFUGA{publicfunctiona():parent{returnnewparent();}}(newHOGE())->a();// FUGAになる

同様にstaticも書けるようにします。`
これは遅延静的束縛のほうのstaticです。

// PHP7までsyntax errorclassTest{publicfunctioncreateFromWhatever($whatever):static{returnnewstatic($whatever);}}

個人的には遅延静的束縛を全く使わないので、何が嬉しいのかよくわかりません。

Variable Syntax Tweaks

賛成47反対0で受理。
変数構文のRFCです。

PHP7.0において変数構文の大規模なリファインがありましたが、そのときに見過ごされていた一部の構文についての対応です。
とあるのですが、見過ごされていたくらいなのですごい細かくて気付かないくらいの変更です。
また、これまではシンタックスエラーだった構文が有効になるという変更なので、互換性のない変更はありません。

$bar="bar";echo"foobar"[0];// OKecho"foo$bar"[0];// PHP7までsyntax error、8からOKecho__FILE__[0];// PHP7までsyntax error、8からOK

DOM Living Standard API

賛成37反対0で受理。
DOMの更新についてのRFCです。

PHPのDOMは、かなり昔にW3 Groupが作ったDOM Level 3に準拠して作られています。
その後DOMはLiving Standardとなり、WHATWGが管理するようになりました。
当時よりだいぶ改善されているので、その変更を取り込みます。

まあでもDOM面倒臭いからあんまり使わないよね。

Always generate fatal error for incompatible method signatures

賛成39反対3で受理。
メソッドシグネチャのRFCです。

継承時にLSP原則に違反する書き方をすると、PHP7では致命的エラーもしくはE_WARININGのどちらかになります。

// こっちは致命的エラーinterfaceI{publicfunctionmethod(array$a);}classCimplementsI{publicfunctionmethod(int$a){}}// こっちはE_WARNINGclassC1{publicfunctionmethod(array$a){}}classC2extendsC1{publicfunctionmethod(int$a){}}

これを全ての場合において致命的エラーに統一します。

むしろ何故いままで致命的エラーになっていなかったんだ案件。

Arrays starting with a negative index

賛成17反対2で受理。
マイナススタートの配列インデックスが使えるようになります。

$arr=[-10=>'a'];$arr[]='b';// PHP7.4まで0、PHP8から-9

いったい誰が得するのかよくわかりません。

Weak maps

賛成25反対0で受理。
弱いマップです。

PHP7.4で弱い参照が導入されましたが、現実的には弱いマップのほうが使われてるよ、ってことで導入されるようです。

私には、普通のマップではなくこっちを使う場面が思いつかない。

get_debug_type

賛成42反対3で受理。
get_debug_typeです。

一言で言うとgettype + get_classです。
オブジェクトであればクラス名を、プリミティブ型であれば型名を取得できます。

gettype(1);// integerget_class(1);// TypeErrorget_debug_type(1);// intgettype(newstdClass);// objectget_class(newstdClass);// stdClassget_debug_type(newstdClass);// stdClass

いちいち使い分ける必要がなく、またintegerとかいう正しくない型名が入ってきたりしないので便利です。

Don't automatically unserialize Phar metadata outside getMetadata()

賛成25反対0で受理。
Pharメタデータの処理方法に関するRFCです。

PHP7では、file_exists("phar://path/to/phar.ext")のようにPharファイルを探すだけで、自動的にそのメタデータが展開されてしまいます。
これによって、いわゆる安全でないデシリアライゼーション攻撃が可能になります。

そこで、Phar::getMetadataを手動で呼び出さない限り、メタデータを展開しないように修正します。

またgetMetadataはメタデータの展開をunserializeで行っています。
そこで引数$unserialize_optionsを追加し、デシリアライズする方法を任意に変更できるようにします。

でも、Cookieなど外部で汚染できるデータではなく、サーバに置いてあるPharファイルが汚染されている時点でアウトな気がしてならないんだけどどうなのでしょうかね。

Add support for CMS

投票なし。
CMSサポートのRFCです。

CMSといってもContents Management SystemではなくRFC5652のCryptographic Message Syntaxです。
openssl_pkcs7_encryptに相当するopenssl_cms_encryptなどの暗号化・復号する関数が実装されます。

RFCを立てはしたものの、OpenSSLの基礎機能だし投票いらんじゃろということで投票なしに採用されたようです。

感想

おい誰だよPHP8たいしたことないんじゃねえのとか言った奴は。
相当にすごいことになっています。

アトリビュートのあたりは少々見通しが不透明ですが、それ以外は順当に実装されることでしょう。
これらを使うことで、PHP8では書きやすく、読みやすく、そして高速なプログラムを作ることができるようになります。
そのぶんだけ、これまで適当な書き方をしていたコードは移植がたいへんになりそうですね。

なお、このリストは、RFCが立って投票が受理され、導入されることが決定したものだけの一覧です。
これ以外にもバグ修正や、RFCを立てるほどでもないちょっとした変更なども多々入っているはずです。
PHP8によって、PHPはかなり完成に近づいてきた感があります。
これ以上に欲しい大きなものってジェネリクスくらいじゃね?

…なんて言ってたら次回もまた色々突っ込まれてきそうですけどね。

先日Microsoftが企業としてPHP8をサポートしないという残念なお知らせがありましたが、今のところは有志によってWindows版ビルドも迅速に提供されています。
PHP8.0ではさしたる影響はないでしょう
まあ今後どうなるかはわかりませんし(Windows版固有バグなど)、とっととWSL2に移行しろって話なのかもしれませんが。
でもローカルなんかXAMPPで十分なんだよめんどくせーコマンドなんていちいち打ちたくねーんだよもっと手抜きさせろ。

Safariは危険なJavaScriptに対応しない

$
0
0

先日Appleが、Safariは幾つかのWebAPIに対応しないと公言しました
日本語記事も出ています。

しかし、どのサイトも対応しないAPIの一覧を並べてはいるのですが、それぞれのAPIが具体的にどのようなものなのかを記載した記事が見当たらなかったので、以下はそれらについて調べてみたものです。

対応しない理由

a threat to user privacy、すなわち、あくまでブラウザフィンガープリントなどの手段によって個人を特定・追跡できてしまうからという理由です。
セキュリティ的にも問題なAPIが並んでいるのですが、そちらは理由ではありません。

対応しないWeb API

Web Bluetooth

caniuse
RFC
非公式日本語訳

01.png

解説

ブラウザからBluetoothを通して接続先のデバイスにアクセスすることができます。
データ転送形式はGATTで、データを取得するだけではなく送り込むこともできます。

接続先の機器にもよりますが、接続可能なデバイスの例はデジカメだったり心拍計だったり血圧計だったりと、そりゃもう個人情報の塊ですから、物によってはひとつでも許可した時点で完全に個人を特定できるようになるでしょう。

Qiita内関連記事

Web Bluetooth API で BLE(Chrome と micro:bit をつなぐ)
micro:bitとWebBluetoothで通信してみました

Web MIDI API

caniuse
RFC

02.png

解説

ブラウザからMIDI機器にアクセスすることができます。

そもそもブラウザをMIDI機器に繋ぐなんてこと、普通の人はしません。
RFCにもFew systems will have significant numbers of MIDI devices attached『個体識別できなくなるほどたくさん接続されるMIDI機器はない』とか書かれるくらいですし。
機器のメーカーや機種名、バージョン、そしてその他のフィンガープリントを組み合わせれば、ほぼ完全に特定できるでしょう。
使ってる人が多いから識別できないGamepad APIとは対照的ですね。

また、音を鳴らしてそれを別のブラウザで聞き取ることができれば、PCとモバイルを紐付けることもできそうです。

Qiita内関連記事

WebAudio/WebMIDI API Advent Calendar 2017 Advent Calendar 2017
WebAudio Web MIDI API Advent Calendar 2016
Web MIDI APIでシンセサイザーの入力を受け取ってみた
Web MIDI API による Web アプリと DAW の連携

Magnetometer API

caniuse
RFC
非公式日本語訳

03.png

解説

磁力センサです。

そもそもこんなものをブラウザで扱う必要があるのかというのもありますが、RFCでも妙に具体的に問題となりそうな例を出しています
・建物などの磁場変動から、スマホの所有者の位置と移動を理論上特定することができる。
Geolocation APIの位置情報とMagnetometer APIのデータの差異から、ユーザがVPNを使っているかどうかがわかる。
・場合によってはキーストロークも取得可能。

そんなに懸念があるんだったら最初から作るなよ。

Qiita内関連記事

見当たらず。

Web NFC

caniuse
RFC

04.png

解説

ブラウザからNFCにアクセスできます。

今どきのスマホであれば、本体は最初から対応しています。
PCであればNFCリーダー/ライターが必要となります。

実装例は、【Web NFC】JavaScriptでNFCタグのデータを読み書きしてみたなどで見ることができます。
思いきりNFCにデータを書き込んでいますね。
これにより個人の特定、複数ブラウザの紐付けが可能になります。

Qiita内関連記事

見当たらず。

Navigator API: deviceMemory

caniuse
RFC

05.png

解説

メモリ量を取得します

取得できる値は厳密値ではなく、0.5・1・2・4・8のような概算値です。
そのため、よっぽど変な仕様のレア端末でもないかぎり、特定の懸念はさほど大きくないのではないかと思われます。
もちろん他情報と組み合わせることで絞り込み精度が上がるのは間違いありませんが、それはこれ以外のどのような情報も同じことです。
このAPIがあえて単独で挙げられている理由はよくわかりませんでした。

まあ、そもそもなんでブラウザごときに搭載メモリ量渡さにゃならんのだっていう話ではありますが。

Qiita内関連記事

Webブラウザの世界から始めるIoT入門 #gigttt

NetworkInformation API

caniuse
RFC

06.png

解説

ネットワーク接続状況を取得します。

NetworkInformation.typeで"wifi"や"bluetooth"などの接続種別、NetworkInformation.effectiveTypeで最近繋いだネットワークと接続速度を取得したりできます。
またonchangeイベントで、接続先ネットワークの変更を感知することもできます。
遅い環境では画質を荒くしたりと、速度によって送り込むコンテンツを変更するようなことを考えているみたいですが、ゴミが送り付けられる未来しか見えない。

接続種別と応答速度からフィンガープリントが可能になります。
またonchangeイベントの発生頻度や発生時刻から、いつまで職場にいて何時に帰宅したというように行動を推測することも可能です。

ちなみにこの問題への対策は、"they should disable JavaScript, monitor that all outbound requests are made to trusted origins, and make diligent use of anonymizing VPN/proxy services."『JavaScriptを無効にする、全てのアウトバウンド要求が信頼された発信元であるか監視する、匿名VPNを使用する』だそうです。
正気なん?

Qiita内関連記事

見当たらず。

Battery Status API

caniuse
RFC
非公式日本語訳

07.png

解説

バッテリの残量、および充電状態などを取得するAPIです。

2016年にはフィンガープリントとして利用可能という論文が発表されています。
またかなり限定的ではありますが、バッテリの消費状況から位置を割り出すことにも成功しています

そのためか本APIは既にWeb標準から削除されており、かつては対応していたFirefoxもあえて非対応に戻しています
従って、今さらあえて名指しで出すようなAPIでもない気がしますね。

Qiita内関連記事

Fx0でBattery Status API

Web Bluetooth Scanning

caniuse……なし?
RFC
ChromeStatus

解説

デバイスの周囲にあるBluetoothデバイスをスキャンできるようになるという……この時点で有り得ない。
そりゃもう世界中にビーコン置いて回りますよ。

Chrome84時点ではフラグenable-experimental-web-platform-featuresを有効にしないと使用することができませんが、有効にすべきではなく一生殺しておくべき機能です。

Qiita内関連記事

見当たらず。

AmbientLightSensor API

caniuse
RFC
非公式日本語訳

08.png

解説

環境光センサの値を読み取ることができます。
Generic Sensor APIというセンサ関連の汎用APIの機能の一部という位置付けのようです。

規則的な生活をしている人ほど危険です。
毎日同じ時間に同じ光度であれば同一人物である可能性が高いでしょう。
二つのデバイスが似たような光度変化をすれば、同一人物あるいは家族程度に近い人物であると判断できるでしょう。

懸念点には『踏んだことのあるリンクは黒、未訪問のリンクは白で表示することで光度を変え、訪問済みか否かを識別する』みたいなことまで書かれていました。

visitedの色とか昔は普通に取れていたんですよね。
牧歌的な時代だった。

Qiita内関連記事

見当たらず。

EME Extension: HDCP Policy Check

caniuse……なし?
RFC……なし?
ChromeStatus
GitHub

解説

Encrypted Media Extensionsの追加機能のひとつで、メディアファイルのHDCPステータスをブラウザから見ることができるようになるみたいです。
HDCPはよく見るところだとHDMI接続とかで使われていて、PS3やAndroidをPCに繋ぐと画面映らん!!ってなる面倒で邪魔なやつです。

解説によると、この機能のプライバシー上の懸念は、EMEと同じ仕組みを使っているのでEMEと同じだということでした。
では、Appleがあえてこの機能だけをあえて非対応にする理由は何なのでしょうか。
正直EME自体が何言ってるか全然わからんので、何がどう危険なのかよくわかりませんでした。
識者あとよろ。

Qiita内関連記事

見当たらず。

Proximity API

caniuse
RFC

09.png

解説

近接センサです。
通話中にスマホから耳を離すと画面がついたり、逆に耳元に持っていくと画面が消えたりしますが、あれです。
距離の変化をブラウザに通知します。

実際は近付いた/離れたの変化どころか、具体的にセンチメートル単位での距離まで取得できるようです。
こんな情報をブラウザに渡すなんて有り得ないですね。

しかしこれをフィンガープリントとして使うのは難しそうな気がもしますが、懸念点にはユーザ識別・フィンガープリントの危険があると書かれています。
どのようなシナリオが想定されているのかは、よくわかりませんでした。

Qiita内関連記事

見当たらず。

WebHID API

caniuse……なし?
RFC

解説

Human Interface Device、すなわち人間が操作する各種デバイスへのアクセス手段を提供するAPIです。

ゲームパッドやMIDI機器などは、それぞれ専用のAPIによってJavaScriptからアクセスが可能です。
しかし、そのような対応は今のところ機器ごとに個々に行わなければならず、今後も追加したい機器が出るたびにAPI策定・実装ってしていたら大変です。
そこで、このAPIでは特定機器に囚われない、HIDデバイスへの汎用的なアクセス手段を提供します。

懸念点が3000文字以上ある時点でねーわってかんじですが、たとえばカメラやマイクなどが付いているデバイス、医療機器などに汎用アクセスされたらたまったものではありません。

またWebで公開されることを想定していない、任意にプログラムを登録できるデバイスが繋がっている可能性もあります。
たとえば今のところブラウザはキーボードの入力をキャッチすることはできますが、逆にキーボードに能動的に何かを働きかけることはできません。
このAPIではそれが可能になり、キーボードの種類によってはキーロガーを仕込むこともできるようです。
その対策は、『ユーザが同意しなければマクロを動かさないようにデバイスのメーカーが対応する』

不山戯けてんのかこれ?

Qiita内関連記事

見当たらず。

Serial API

caniuse……なし?
RFC

解説

ブラウザからシリアルポートに……はい終わり。

Qiita内関連記事

見当たらず。

WebUSB

caniuse
RFC

10.png

解説

ブラウザからUSBポートに……はい終わり。

Qiita内関連記事

WebUSBを使ってブラウザのJavaScriptからArduinoを制御してみよう!
micro:bitでWebUSBを使う

Idle Detection

caniuse……なし?
RFC……なし?
GitHub
ChromeStatus

解説

アイドル状態を通知します。

スクリーンセーバーが起動した、画面がロックされた、などのイベントをブラウザが検知できるようになります。
これにより、複数のWebサイトで同時に画面ロックイベントが発生すると、それは同じブラウザ、あるいは一つの端末である可能性が高い、すなわち同一人物であると判断できます。

この緩和策としてイベント発生をランダムに遅延させるという方法が考えられましたが、30秒遅らせたところで長期収集することで十分に特定可能だということがわかったので却下されました。

その代替策として策定されたのが『パーミッションを要求する』はい役に立たないやつ来た。

Qiita内関連記事

見当たらず。

Geolocation Sensor

caniuse……なし?
RFC
非公式日本語訳

解説

位置情報センサです。

既存のGeolocation APIの拡張という立場です。

何が拡張されたかというと、Geolocation APIでは不可能だったバックグラウンド動作が可能になります。
すなわち、いったん他の手段で端末を特定さえしてしまえば、自宅と職場と移動ルートを完全に把握されるということです。

このAPI単体での個人特定は余程の田舎でもないかぎり難しいかもしれませんが、他のフィンガープリントと組み合わせることで、とてつもなく危険なAPIになります。

Qiita内関連記事

見当たらず。

感想

なんでもかんでもブラウザで実行できてしまうとネイティブアプリに誘導できなくなって稼ぎが減ってしまう、という打算はもちろん有るでしょう。
しかし、それを別としても、ほとんどは対応しないのが当然すぎるAPIばかりですね。
特にNetworkInformation APIやらWeb Bluetooth Scanningなど、発想するまでは自由ですが、実装する時点で正気を疑うレベルです。
そんなわけでSafariがこれらのAPIから距離を取るのは、まあ妥当ですね。

なお、これらのAPIのほとんどは、Googleが先行して策定・開発し、W3CにWeb標準として提案するという形になっています。
すなわち、Chromeにはこれらの多くが既に実装されているということです。
おおこわいこわい。

過去の自分に教えなければならないVSCodeのショートカット

$
0
0

以下はVitor Paladini ( Twitter / GitHub / dev.to ) によるVS Code shortcuts that I would teach myself if I had a time machine with limited fuelの日本語訳です。

VS Code shortcuts that I would teach myself if I had a time machine with limited fuel

「やあ俺だ。黙って聞いてくれ。時間がないんだ。」
「待って待って、お前誰だ。どうやって部屋に入ってきた。あとどうして俺そっくりなんだ。」
「俺は未来のお前だ。ここまで来たのは、特別にVSCodeのショートカットを教えるためだ。さあいくぞ」
「正気か?ロト6の番号でも株式情報でもなく、わざわざVSCodeのショートカットを教えにやってきたのか?」
「聞くんだ。俺にはもう、これ以上の力は残っていないんだ。座ってよく聞け。これは重要な…」
「ショートカットを紙にでも書くのではいかんのか?」
「シャラップ!黙って聞け!」
「わかったわかった」
「それではまず、エクスプローラでファイルをクリックするのを辞めて、Ctrl+Pを押すんだ。そしてファイル名を入力せよ。あとファイル名に続けて:100って書くと100行目が開くよ。」
01.gif
「おお!未来ではホログラム画像表示できるんだな、これはわかりやすい。」
「あとファイルエクスプローラのマウスナビゲートも辞めて、使うのはCtrl+Shift+Sと矢印キーだけにするんだ。」
02.gif
「こいつぁクールだ…」
「さらにだな、やっぱり編集エリアに戻りたくなったらら、編集エリアをクリックするんじゃなくてCtrl+1を押すんだ。さっきまで開いてたエリアに戻るぜ。」
03.gif
「最高にクールだな!」
「編集エリアでファイルを切り替えるときはCommand+Shift+[]だぜ。WindowsではAlt+左右だ。おまえは多くの時間を救うことができる。」
04.gif
「これで多くの時間が変わるとは考えにくいけど……」
サイドバーの表示非表示を切り替えるにはCtrl+Bだ。おまえは多くのスペースを救うことができる。」
05.gif
「こいつは知ってたぜ!」
「スクロールしすぎるのを防ぐためには、Ctrl+Gを押して直接目的の行に移動するんだ。」
06.gif
「わかったよ。」
「あるテキストが現れる全ての箇所を選択したいときはCtrl+Shift+Lだぜ。」
07.gif
「はい。次おねがいします。」
「今のタブを閉じるときはCtrl+Wで、閉じたタブをもう一度開きたいときはCtrl+Shift+Tだ。」
「それはよく使ってる。特に目新しくは…」
08.gif
「おっと。もう戻らないといけない時間だ。最後にCtrl+Shift+Hで検索と置換ができるぜ。それではさらばだ!」
09.gif
「待って!まだ聞きたいことがたくさんあるんだ!タイムマシンをどうやって手に入れたんだ?タイムトラベルはパラレルワールドになるのかい?49ersがスーパーボウルで再び優勝する時は来るのかい?へいちょっと!!」

コメント欄にも素晴らしいショートカットがたくさん載ってるから見ていってね!

・サンプルコードはForemです。
・gifは680*416、12FPSでKapで録画したよ。
・VSCodeのテーマはCobalt2、フォントはEnvy Code Rだよ。

コメント欄

「ファイルを編集したりファイルを移動したりスクロールしたりしたあと、元に戻るにはCtrl+-、前進はCtrl+Shift+-。べんり。」「こいつ知らなかった!すごい!」「マウスの前進後退ボタン使ってる。」
「次に過去に戻るときは、過去の俺にShift+Alt+Fを教えてやってくれ。コードの自動フォーマットだ。」「フォーマットし忘れないように、セーブ時に自動フォーマットするようにVSCを設定するともっといいぞ。」
Ctrl+Lで今いる行を選択できる。連打すると複数行選べる。さらにAlt+↑↓で選択行をそのまま移動できる。コピペの必要なっしんぐ。」「似たようなショートカットでShift+Alt+↑↓があるぜ。選択行をペーストしてくれる。」
Ctrl+Dで今カーソルがあるところの単語を選択する、これ本当に便利。」
Ctrl+`でターミナルの開閉、Ctrl+Shift+`で新しいターミナル、これ毎日使ってる。」
「ビューの切り替えにCtrl+Qをよく使う。」
Ctrl+]でブラケットの開始終了位置にワープする。べんり。」「うちの環境だとタブが入るんだけど、キーマッピングが違うのかな?」
Alt+1Alt+2、…で1番目、2番目、…のタブを直接開けるよ。Chrome(Ctrl+n)、Gnomeターミナル(Alt+n)とかも同じように動くので覚えておくと便利。」
Ctrl+Shift+Pはマスターキーだ。キーボードショートカットを含む全てのコマンドを検索できるよ。」
「"タイムマシンがあるのに何故私は未だにコーディングしているのか"を問うべきだった。」「誰かがタイムマシンのファームウェアを改変したんだろう。」

感想

本文の勢いにつられて訳してしまったわけですが、中身は普通に参考になるショートカット紹介記事です。
本文とあとコメント欄にも有用なショートカットがたくさんあって、是非すぐにでも取り入れたいものです。

まあしかし、これらのショートカットを全て覚えて、マウスを一切使わずキーボードだけで作業できるようになるかというと、できるようになれる気が到底しないのは何故だろう。

あと、基本的にWindowsとMacでショートカットは同じなのですが、何故か極一部だけ全く異なるコマンドがあってたいへんですね。
私はMacを全く使わないから関係ないのですが。

WindowsでXAMPPにPHP8.0.0β2をインストールする

$
0
0

先日PHP8.0.0α1でJITを試したときはPHP8をXAMPP経由で起動することができなかったのですが、今回動かすことができたので、その方法の共有です。
今回試したのはPHP8.0.0β2です。

XAMPPインストール

まずローカルのXAMPPを削除。
公式サイトからWindows向けXAMPPの最新版をダウンロードしてインストール。
既にPHP7.4.x版が入っているようなら、そのままでもいけると思います。

PHP8.0.0インストール

Windows版ダウンロードページからVS16 x64 Thread Safeをダウンロード。
適当に解凍。
path\to\xampp\phpディレクトリにだばぁ!と上書き。
php.ini-developmentを既存のphp.iniに上書き。
extension=mbstringとかdate.timezoneとかを好きに設定する。

Apacheの設定

path\to\xampp\apache\conf\extra\httpd-xampp.confをエディタで開く。
php7ts.dllphp8ts.dllに置換。
php7apache2_4.dllphp8apache2_4.dllに置換。
php7_modulephp_moduleに置換。

起動

XAMPPコントロールパネルから起動。
http://localhostにアクセス。
これでPHP8.0.0が動いているはず。

01.png

注意点

httpd-xampp.confphp_moduleがポイントです。
ここを過去の類推からphp8_moduleにすると何故か動きません。

これUPGRADINGにしっかり書かれているので、きちんと調べればわかります。

では何故α1でJITを試した時には気付かなかったのかというと、PHP8.0.0α1のUPGRADINGには全く書かれてなかったからなんじゃ!
PHP8.0.0α2のUPGRADINGからしれっと追加されています。

ちなみに、この設定はApacheからPHPを呼び出すときの設定なので、Apacheを使わず直接コマンドラインからPHPを呼び出すのであれば不要です。
先日のJIT実験は、そうやって直接PHPを呼び出したということです。

Apache経由のJITが死ぬ

ということで今度はApache経由でのJITを試してみようと思ったわけですが、なんか動きませんでした。
opcache.jit_buffer_sizeが設定されていると、何故かApache経由でアクセスするとERR_CONNECTION_RESETになって死にます。
これもコマンドライン経由だときちんと動くので、おそらくこれもhttpd-xampp.confあたりの問題だと思うのですがよくわかりませんでした。
XAMPPかApacheかどこかがサンプル設定上げてくれるとありがたいんですけどね。

PHP CS Fixerが死ぬ

PHPのデフォルトをPHP8にするとPHP CS Fixerがエラーを吐くようになります。
VSCodeだとPHP CS Fixer: php general error.としか言わないから何が原因なのかわからねえ。

直接実行すればメッセージを確認できます。

$ "path\to\php\php.exe""path\to\php-cs-fixer-v2.phar"
PHP needs to be a minimum version of PHP 5.6.0 and maximum version of PHP 7.4.*.

ええ…
現行最新のv2.16.4を使ってもこう言われます。
RCが出たらまた来てだそうです。ひどい。
バージョン見て門前払いじゃなくて、せめて動かせるけど保証はしない、ってなりませんかね。

VSCodeのPHPフォーマッターはたくさんありますが、その多くはPHP CS Fixerのラッパーです。
従ってPHP CS Fixerが対応しないことにはまともに開発できないため、まだまだ環境を切り替えるには早いですね。
自力で整形しているフォーマッターのプラグインがあるのかはよくわかりませんでした。

どうしても早く使いたいなら、自力で対応しているPHPstormなどを使ってみるといいかもしれません。


TypeScriptの型上級チートシート

$
0
0

Original article: https://www.ibrahima-ndaw.com/blog/advanced-typescript-cheat-sheet/

以下はIbrahima Ndaw( Twitter / GitHub / LinkedIn / Webサイト )によるTypeScriptの解説、Advanced TypeScript Types cheat sheet (with examples)の日本語訳です。
リンクなどは元記事のままであり、和訳にあたり変更していません。

Advanced TypeScript Types cheat sheet (with examples)

TypeScriptは型付き言語であり、変数、関数の引数および返り値、オブジェクトのプロパティに型を指定することが可能です。

この記事では、TypeScriptの型の高度な使い方を例示付きで紹介します。

Sorry for the interrupt!

TypeScriptを総合的に学びたい人には、こちらのベストセラーコースを強くお勧めします。
Understanding TypeScript - 2020 Edition
これはアフィリエイトリンクなので、よかったら応援してね。

Intersection Types

交差型とは、複数の型をひとつに結合した型です。
すなわち、型Aと型B、もしくはさらに他の型をマージして、それら全てのプロパティを持ったひとつの型を得ることができます。

typeLeftType={id:numberleft:string}typeRightType={id:numberright:string}typeIntersectionType=LeftType&RightTypefunctionshowType(args:IntersectionType){console.log(args)}showType({id:1,left:"test",right:"test"})// Output: {id: 1, left: "test", right: "test"}

見てのとおり、交差型はLeftTypeとRightType両方の要素を持っています。
交差型をつくるには&で結合するだけです。

Union Types

Union型は、与えられた型のうち何れかの型となることができます。

typeUnionType=string|numberfunctionshowType(arg:UnionType){console.log(arg)}showType("test")// Output: testshowType(7)// Output: 7

関数showTypeは、string型もしくはnumber型いずれかの値を引数として受け付けることができます。

Generic Types

ジェネリック型とは、与えられた型を再利用する手段です。
引数の型を変数のようにキャプチャすることができます。

functionshowType<T>(args:T){console.log(args)}showType("test")// Output: "test"showType(1)// Output: 1

ジェネリック型を生成するには、関数名に<>で括った型名T ( 実際は任意の名前でよい ) を指定します。
以下に、関数showTypeを異なる型で呼び出す例を示します。

interfaceGenericType<T>{id:numbername:T}functionshowType(args:GenericType<string>){console.log(args)}showType({id:1,name:"test"})// Output: {id: 1, name: "test"}functionshowTypeTwo(args:GenericType<number>){console.log(args)}showTypeTwo({id:1,name:4})// Output: {id: 1, name: 4}

ジェネリック型Tを受け取るインターフェイスGenericTypeを定義しました。
これは再利用可能なので、ひとつめのGenericTypeはstring型の値を受け取り、ふたつめはnumber型を受け取っています。

interfaceGenericType<T,U>{id:Tname:U}functionshowType(args:GenericType<number,string>){console.log(args)}showType({id:1,name:"test"})// Output: {id: 1, name: "test"}functionshowTypeTwo(args:GenericType<string,string[]>){console.log(args)}showTypeTwo({id:"001",name:["This","is","a","Test"]})// Output: {id: "001", name: Array["This", "is", "a", "Test"]}

ジェネリック型を複数渡すこともできます。
上の例では二つのジェネリック型TとUを渡しています。
interfaceを使用することで、異なる型の引数を渡す関数が提供できるようになりました。

Utility Types

TypeScriptでは、型を容易に操作することができるように便利な組込ユーティリティ型が提供されています。
これらを使うときは、変換したい型を<>に入れて渡します。

Partial

Partial<T>

Partial型は、該当する型の全てのプロパティをオプショナルにすることができます。
これはすなわち、全てのフィールドに?を追加するようなものです。

interfacePartialType{id:numberfirstName:stringlastName:string}// firstNameがstringからstring?になるfunctionshowType(args:Partial<PartialType>){console.log(args)}showType({id:1})// Output: {id: 1}showType({firstName:"John",lastName:"Doe"})// Output: {firstName: "John", lastName: "Doe"}

関数showType()の引数としてPartialType型を渡していますが、プロパティをオプショナルにするためにPartialユーティリティ型を通しています。
これだけで、PartialType型の全ての値がオプショナルになりました。

Required

Required<T>

Partial型とは「逆に、Required型は全てのプロパティを必須にします。

interfaceRequiredType{id:numberfirstName?:stringlastName?:string}// firstNameがstring?からstringになるfunctionshowType(args:Required<RequiredType>){console.log(args)}showType({id:1,firstName:"John",lastName:"Doe"})// Output: { id: 1, firstName: "John", lastName: "Doe" }showType({id:1})// Error: Type '{ id: number: }' is missing the following properties from type 'Required<RequiredType>': firstName, lastName

Requiredユーティリティ型を通すことによって、オプショナルであるはずのRequiredType型の全ての値が必須になります。
プロパティを省略した場合、TypeScriptはエラーを発生させます。

Readonly

Readonly<T>

Readonlyユーティリティ型は、全てのプロパティを変更不可能にします。

interfaceReadonlyType{id:numbername:string}functionshowType(args:Readonly<ReadonlyType>){args.id=4console.log(args)}showType({id:1,name:"Doe"})// Error: Cannot assign to 'id' because it is a read-only property.

Readonlyユーティリティ型によって、ReadonlyType型の全ての値は再割り当て不能になります。
いずれかのフィールドに新しい値を設定しようとすると、エラーになります。

もっと単純に、プロパティの前にreadonlyキーワードを付けて再割り当て不能にすることもできます。

interfaceReadonlyType{readonlyid:numbername:string}

Pick

Pick<T, K>

Pickユーティリティ型は、元の型からいくつかのプロパティを選んで新たな型を生成します。

interfacePickType{id:numberfirstName:stringlastName:string}// PickTypeのうちfirstName,lastNameだけを使った新たな型functionshowType(args:Pick<PickType,"firstName"|"lastName">){console.log(args)}showType({firstName:"John",lastName:"Doe"})// Output: {firstName: "John"}showType({id:3})// Error: Object literal may only specify known properties, and 'id' does not exist in type 'Pick<PickType, "firstName" | "lastName">'

これまでに見てきたユーティリティ型とは少々異なる構文で、二つの引数が必要です。
Tは元の型、そしてKは抽出したいプロパティです。
複数のフィールドを|で区切ることによって、複数のフィールドを抽出することも可能です。

Omit

Omit<T, K>

Omitユーティリティ型はPickのちょうど反対で、必要なプロパティを選ぶのではなく不要なプロパティを削除します。

interfacePickType{id:numberfirstName:stringlastName:string}// PickTypeのうちfirstName,lastNameを使わない新たな型functionshowType(args:Omit<PickType,"firstName"|"lastName">){console.log(args)}showType({id:7})// Output: {id: 7}showType({firstName:"John"})// Error: Object literal may only specify known properties, and 'firstName' does not exist in type 'Pick<PickType, "id">'

Pickと同じ使い方で、元となる型から削除するプロパティを指定します。

Extract

Extract<T, U>

Extractユーティリティ型は、T型のプロパティのうち、U型に代入可能なプロパティを抽出します。
2つの型に共通するプロパティを取り出すと考えてよいでしょう。

interfaceFirstType{id:numberfirstName:stringlastName:string}interfaceSecondType{id:numberaddress:stringcity:string}typeExtractType=Extract<keyofFirstType,keyofSecondType>// Output: "id"

上の例では、2つの型が同じプロパティidを持っています。
この型にExtractを使用することで、両方に共通するプロパティidを取り出すことができます。
共通するプロパティが複数存在する場合は、その全てが抽出されます。

Exclude

Exclude<T, U>

Excludeユーティリティ型は、T型のプロパティのうち、U型に代入可能なプロパティを除外した型を生成します。

interfaceFirstType{id:numberfirstName:stringlastName:string}interfaceSecondType{id:numberaddress:stringcity:string}typeExcludeType=Exclude<keyofFirstType,keyofSecondType>// Output; "firstName" | "lastName"

FirstTypeのプロパティfirstNamelastNameはSecondTypeには存在しないため、Excludeで取り出すことができます。
SecondTypeの値addresscityは出てきません。

Record

Record<K,T>

このユーティリティは、T型の値の集合を作るために役立ちます。
ある型のプロパティを別の型にマッピングする際に、非常に便利です。

interfaceEmployeeType{id:numberfullname:stringrole:string}letemployees:Record<number,EmployeeType>={0:{id:1,fullname:"John Doe",role:"Designer"},1:{id:2,fullname:"Ibrahima Fall",role:"Developer"},2:{id:3,fullname:"Sara Duckson",role:"Developer"},}// 0: { id: 1, fullname: "John Doe", role: "Designer" },// 1: { id: 2, fullname: "Ibrahima Fall", role: "Developer" },// 2: { id: 3, fullname: "Sara Duckson", role: "Developer" }

Recordの動作はシンプルです。
上の例ではキーの型がnumberなので、0,1,2と数値を指定しています。
値はEmployeeType型となっているので、id・fullname・roleを持つオブジェクトが必要です。
文字列を与えたりするとエラーになります。

NonNullable

NonNullable<T>

型Tからnullとundefinedを消し去ります。

typeNonNullableType=string|number|null|undefinedfunctionshowType(args:NonNullable<NonNullableType>){console.log(args)}showType("test")// Output: "test"showType(1)// Output: 1showType(null)// Error: Argument of type 'null' is not assignable to parameter of type 'string | number'.showType(undefined)// Error: Argument of type 'undefined' is not assignable to parameter of type 'string | number'.

NonNullableユーティリティは、引数からnullとundefinedを消し去った新たな型を生成します。
その型にNullableな値を渡すと、TypeScriptはエラーを発します。

なお、tsconfigファイルにstrictNullChecksを指定すると、自動的に全ての型にNonNullableが適用されます。

Mapped types

Mapped typesは、既存のモデルを流用しつつ、各プロパティを新しい型に変更することができるようになります。
先に解説したユーティリティ型も、一部の実体はMapped typesです。

typeStringMap<T>={[PinkeyofT]:string}functionshowType(arg:StringMap<{id:number;name:string}>){console.log(arg)}showType({id:1,name:"Test"})// Error: Type 'number' is not assignable to type 'string'.showType({id:"testId",name:"This is a Test"})// Output: {id: "testId", name: "This is a Test"}

StringMap<>は、渡された型が何であれ、とりあえず文字列型にします。
従って、showTypeに渡す型はnumberではなくstringとなり、number型を渡した場合はエラーが出ることになります。

Type Guards

Type Guardsを使うと、変数やオブジェクトの型を演算子で判定することができます。

typeof

functionshowType(x:number|string){if(typeofx==="number"){return`The result is ${x+x}`}thrownewError(`This operation can't be done on a ${typeofx}`)}showType("I'm not a number")// Error: This operation can't be done on a stringshowType(7)// Output: The result is 14

上記コードでは、typeofを用いて受け取った引数の型をチェックしています。
条件で型ガードすることができました。

instanceof

classFoo{bar(){return"Hello World"}}classBar{baz="123"}functionshowType(arg:Foo|Bar){if(arginstanceofFoo){console.log(arg.bar())returnarg.bar()}thrownewError("The type is not supported")}showType(newFoo())// Output: Hello WorldshowType(newBar())// Error: The type is not supported

typeofの例と同様、こちらでは引数の型がFooクラスであるかをチェックします。

in

interfaceFirstType{x:number}interfaceSecondType{y:string}functionshowType(arg:FirstType|SecondType){if("x"inarg){console.log(`The property ${arg.x} exists`)return`The property ${arg.x} exists`}thrownewError("This type is not expected")}showType({x:7})// Output: The property 7 existsshowType({y:"ccc"})// Error: This type is not expected

オブジェクトにプロパティが存在するかどうかはinでチェックすることができます。

Conditional Types

以下では複数の型をテストし、結果に応じてその片方を選択しています。

typeNonNullable<T>=Textendsnull|undefined?never:T

NonNullableユーティリティ型は、型がnullであるかをチェックし、結果に応じて異なる処理をしています。
この例ではJavaScriptの三項演算子を使用していることに注意してください。

コメント欄

dev.toのコメント欄

「わかりやすくてよい記事。GJ!」
「簡潔によくまとめられてる。」
独自のタイプガードの可能性を追求した。」
「もっとはやくPartialを知りたかった。わざわざ自力で書いてたよ。export type ObjectWithOptionalProps<T> = { [key in keyof T]?: T[key] };
「Maybe型type Maybe<T> = T | null;をよく使ってる。」「普通にオプショナルfunction foo(bar?: string)でよくない?」「Maybe型の場合は値が必須というところが異なるよ。」
「TypeScript複雑になりすぎてきたような」「ほとんどは糖衣構文なので使わなければいいだけだぞ」「せやな」
「行末にセミコロンを忘れるな」「セミコロンは必須ではないし使わない方が好き」「ない方がすっきりしててよい」「Google様に逆らうなら不要だね」

感想

TypeScript Guideシリーズの2番目で、主に型の変換について記述された記事です。
他のシリーズも通して読むことで、TypeScriptへの理解がより深まることでしょう。
参考になったらぜひコーヒーを買ってあげましょう。

実際に手を動かしてみたいという場合は、ちょうど最近日本語解説記事が出たtype-challengesなどを試してみるとよいかもしれません。

しかし型に関するいろいろな機能はありますが、実際使うかといったら個人的にはほとんど使っていません。
私はフロントエンドエンジニアではないので本格的にがっつり使ってないからということもありますが、極めてふつーに、型を定義してそれを直接使う程度のことしかやっていませんし、それで不足を感じることもあまりありません。
日頃使ってるのはせいぜいUnion型くらいです。
OmitとかExcludeとか何に使うのか全くわからん。
みんなこういうのバキバキ使いこなしてるんですかね?

Linuxカーネル、その29年の歴史レポート

$
0
0

2020/08/25、Linux Foundation2020 Linux Kernel History Reportというレポートを発表しました。
1991年9月17日の最初のリリースから、2020年8月2日にリリースされたLinux5.8まで、29年におよぶLinuxカーネルの歴史をまとめたものです。

ということでざっくり斜め読みしてみました。
ていうかコピペできないPDFなのどうにかしてくれ。

Linux Kernel History Report

このレポートは、Linuxの全ての歴史を調査したものである。
1991年9月17日に最初のカーネルがリリースされてから、最新の5.8カーネルがリリースされた2020年8月2日までには、BitKeeperとgitに100万件以上のcommit履歴が記録されている。

Kernel Archeology

最初のリリースは88ファイル1万行、2964トークンしかなかった。
当初の痕跡はごくわずかしか残っていないが、vsprintf.cには1991/09/17のLinus自身によるファーストコミットが今も存在する。

01.png

Linux5.8のカーネルの半分以上はこの7年以内に書かれたコードである。

02.png

Impact of Development Process Best Practices

開発のベストプラクティスを推進するため、CIIは2015年にベストプラクティスバッジ制度を作った。
Linuxコアは最初期にこのバッジを取得したプロジェクトのひとつで、2020年6月には最高級称号であるゴールドバッジを取得した。

とか書いてあるんだけど、CIIはLinuxFoundation内の組織なのでどうにもマッチポンプ感が拭えない。

Adoption of Maintainer Hierarchy

1996年の1.3.68で初めてMAINTAINERSファイルがコミットされた。
わずか107行で、メンテナは僅か3人。
Alan Cox、Jon Naylor、そしてLinus Torvalds。

MAINTAINERS
REST:
P: Linus Torvalds
S: Buried alive in email

Linuxカーネル開発の初期の議論は複数のMLでなされていたので、1997年以前の議論については断片的にしか存在していない。
集められた一部についてはhttp://lkml.iu.edu/hypermail/linux/で公開されているが、これにも抜けがあるので、当時のログを持っている人がいたら提供してほしい。

翻って5.8のMAINTAINERSは19033行もあり、そして1501人のメンテナがリストされている。

MAINTAINERS
THE REST
M: Linus Torvalds <torvalds@linux-foundation.org>
L: linux-kernel@vger.kernel.org
S: Buried alive in reporters
Q: http://patchwork.kernel.org/project/LKML/list/
T: git git://git.kernel.org/pub/scm/linux/
kernel/git/torvalds/linux.git
F: *
F: */

Version Control Systems

03.png

BitKeeper以前は、誰がどれくらい貢献していたかについてはあまりはっきりとしていない。
きちんとした履歴は2002年ごろにはじまっていて、それ以前については各文献に残された履歴から推測している。

2005年に色々あってBitKeeperが使えなくなったので、Linusが一からGitを作ってそっちに移行した。

開発者の確保は永遠の課題であり、Linux FoundationもOutreachyやLKMPプログラムといった支援を続けている。
女性コントリビュータを増やす取り組みも行ってきた。

04.png

Removing Unused Code

使用していないコードを削除する取り組みは継続していて、たとえば2018年の4.17では8アーキテクチャ18000行を削除した。

ということらしいのだけど増加量に比べたら微々たる量でしかないな。

05.png

Highly Diversified Corporate Contributors

Linuxカーネルは多くの企業から貢献を受けている。
2007年から2019年まで、1730の企業から780048のコミットがあった。
そのうちトップ20企業が68%を占めている。

06.png

この10年では、毎年400社程度からのコントリビュートがある。

07.png

Release Model with Predictable Release Cycle Cadence

Linuxのリリースモデルは4種類に分類される。
・Prepatch (RC)
・Mainline
・Stable
・Long Term Stable

最新のカーネルはhttps://www.kernel.org/で見ることができる。

リリースサイクルについては多くの議論がなされたが、2011年以降概ね機能するリリースモデルが構築された。
まず2週間のmerge windowから始まり、新機能はテストされてgitリポジトリに導入される。
RC1のタグが振られると結合テスト、デバッグ、最適化のサイクルに入り、品質と安定性が確保されるまでRCが毎週更新される。
新機能がリリースされると、ふたたび次のmerge windowが始まる。

08.png

Improving Automated Testing the Kernel

カーネルのテストはコミュニティの努力によって成り立っている。
自動テストBOTとしてSparse、Smatch、coccicheck、ファジングテストとしてTrinity、syzbotなどが走っている。
これらによって多くのバグが発見されている。

09.png

Stable Release Process

安定版へのリリースはおよそ週1で行われる。

Linux5.7.9を例に取ると、まずリリース候補RCがMLにメールでアナウンスされる。
このRFには166のパッチがあり、それらは全て個別にMLで通知される。
各開発者および自動テストツールがそれらのリリース工法をテストし、結果をメールに返信する。
RCに問題があればRC2、RC3と後続が作成されるが、RC2が作られることはあまり多くない。

自動ビルドBOTは30種以上のアーキテクチャに対してビルドを実行する。
安定版では31アーキテクチャ、56コンフィグに対してビルドが行われる。
そしてLKFT、LTP、Linux Kernel Selftests、Linux Perfなど多くのテストが行われる。
テストが十分に果たされるとリリースされ、その通知がMLに送られる。

Longterm Release Kernels

長期サポートを導入したことにより、組込製品などにおいてLinux人気がさらに高まった。
SUSE、Ubuntu、Red Hatなどのディストリビューションが先駆けて導入して有用性を証明したことで、カーネルにもこの概念が導入されることになった。

安定版カーネルで発見されたバグはまず安定版に適用されるが、LTSにも適用可能であると判断された場合はバックポートされる。
安定版の修正をLTSも適用可能であるかの判定は、Sasha Levinによる機械学習ツールの開発などによって年々改善されている。
2019年には18668件のバックポートが行われたが、これは15年前の安定版カーネルへの変更より多い件数である。

LTSよりさらに長い超長期サポートSLTSを求めている市場もあり、一部の開発者はLinux4.4と4.19をSLTSとしてサポートすることにしている。

Conclusion

現在のLinuxカーネルは、医療機器から宇宙船に至るまで、セキュリティと安全性が必要なあらゆる分野で使用されている。
Linuxを使う前に、適切なセキュリティと安全性を確保できるようインフラを改善していくことが、次の取り組んでいる大きな課題のひとつである。

Linuxカーネルには、OSS業界全体を改善していくためのベストプラクティスを作成し、世界をリードし続けるための素晴らしい基盤が存在している。

Thanks

なんか見切れてるんだけど。

10.png

感想

もはや世界的に無くてはならない存在であるLinuxですが、その成長の軌跡がざっくりわかる興味深い資料でした。
コミッター数もコミット数もコード量も、近年のその飛躍的な増加も、このプロジェクトの重要性をよく示していますね。

ただまあ公的に近い資料だけあって表沙汰にできるような内容しかありません。
の集まったレポートとかもあると楽しそうです。

最近はクラウドだーサーバレスだーと、低レベルOSの存在は裏に隠そう隠そうと躍起になっていますが、何かあるとすぐコマンドとかログとか掘り返さないといけなくなって結局Linuxの知識が必要となるのは今でもあまり変わりません。
OSの存在や種類など気にしなくていいような世界は、本当にいつの日かやってくるのでしょうか。

2020/09/15 PHPerによるPHPerのための「『PHP8』のニュースや記事を語り合う」TechCafe

$
0
0

2020/09/15、ラクスによるPHPerによるPHPerのための「『PHP8』のニュースや記事を語り合う」TechCafe というイベントがありました。

実はひめやかに参加していたので、以下はそのざっくりメモです。

TechCafe

TechCafeとは

エンジニアと技術が交差する憩いの場。
なんかしらの話題でわいわい。

なんでPHP?
ラクスのサービスのうち6個がPHPなので。

対象:PHPを入門後の初級エンジニア~シニアエンジニア

本日のテーマ

vlcsnap-2020-09-15-20h58m37s190.png

出演者

ラクス ふじさわ氏
ラクス 加納氏
ラクス 久山氏
ラクス おおつか氏
白柳隆司氏 https://www.youtube.com/channel/UCv1AIkCCrRB_Tcz_1ZgSoFg/videos
Futoshi Endo氏 https://twitter.com/Fendo181

PHP8の新機能

Union Types

TypeScriptとかでもふつうにある機能。
わりと賛否わかれる。
これまでIDEやら静的解析はPHPDocを使っていたが、PHP構文となることで実行時チェックできるようになる。
コメントの更新忘れで古くなるとかが防げる。
nullとかのエッジケースを明示化できる。

PHPは元々自由型だったので複数の型が入ってくることはよくある。
新規コードよりは、レガシーコードのリファクタに利点があるのではないか。

PHPStanを使ってチェックしているが、これが来たらスマートにできそう。
新しく来た人が見たときに助かるのでよい。

Javaの人はきっと許さない。
強い型言語から来たので、型がないと肩が凝る。

JIT

PHPは、通常はリクエストが来る度にソースコードをOPCodeに変換し、さらにネイティブコードに変換して実行する。

高速化の仕組みとしてOPCacheがある。
OPCodeをキャッシュして、次のリクエストでOPCodeを使い回す。

JITはこの発展系。
ネイティブコードをキャッシュして、次のリクエストでネイティブコードを使い回す。

Webアプリのオーバーヘッドが大半がI/Oなので、Webアプリでは大幅な高速化はなさそう。
高速な計算処理など、これまでC一択、最近はRustだったりした
これをPHPでやるという選択肢の可能性ができる。
Cとちがってメモリ管理とかが簡単なので安全に実装できる。

PHPは遅いってイメージが未だに残ってるので我々がどうにかしていかないと。

php.iniオプションopcache.jitの説明が微妙に間違ってた。

JIT使うと使う容量が増えてみたいな話があったけど、実はJITを有効化するオプションopcache.jit_buffer_sizeが直接容量を指定する設定なのだ。

PHP8のjitをweb以外でつかうとなると現時点でなにか用途がありますか?
画像処理・ゲーム・機械学習とかどうよ?
セキュリティ関連。

The nullsafe operator

APIの返り値などツリー形式データにアクセスする際、if($session !== null)のネストをなくすことができる。

10行くらいが1行にリファクタできるなんて気持ちいい。
全ての言語に広まってほしい。
JavaScriptでも同じRFCが上がってる。

Named arguments

メソッドの引数に名前を付けて渡すことができる。
呼び出し側を見るだけで、目的が一目瞭然。
通常引数は順番が大事だったけど、名前付き引数は順番を入れ替えることもできる。
最後の引数だけ指定したい場合は途中の引数のデフォルト値を調べて渡さないといけなかったけど、それをスキップできる。

これまではデフォルト引数の問題を解決するため配列$optionsを渡すみたいなことをやっていたこともあったが、何を設定できるのかわからなかった。

Union Typesもだが、プロジェクトに入って間もない人にとって助かる機能。

IDEなんかでがんばって実現してるものを取り入れるみたいな傾向が見える。

PHPに求められるものが変わった。
できる人が手の中でちょっとしたものを作るみたいなツールから、大人数で大規模な使用に耐えるシステムに。

Attributes

PHPDocなどに記述していたメタデータを言語仕様として書けるようになった。

たとえばPHPUnitでは@afterのようなアノテーションを書けるが、これは単なるコメントで制約もなく、ライブラリ作者が勝手に決めていた。
取り出しも正規表現とかでがんばって抜き出していた。
言語仕様になるので、リフレクションでアクセスできるようになる。

仕様が二転三転したので、記事によっては古い書き方が残ってたりすることもあるので注意。

PHPUnitは2022年に対応するかも。

アトリビュート自体はライブラリ作者のためのものなので、一般ユーザが気にすることはあんまりない。

Match expression

switch文によく似たmatch式の導入。

break不要、式なので代入できる、複数条件記述できる、厳密な比較。

defaultがないとエラーになると言っていたけど、実際はdefaultがないだけではエラーにならず、どの分岐にも進めなかったときにエラーになる。

単一行でシンプルな代入処理はmatch式、それ以外はswitchがよさそう。

==はもはや死語になっていくんだな

ちょっと動けばいいや的なのなら==でいいんじゃね。
その後大きくなるようなら最初から===がいい。

New mixed type

RFCでは次の用途で用いるとしている。

・型のことを気にしてはいるが、まだ正確に記述していない
・正確に指定することができない
・明確な意図で型を縛っていない

弊社では、リファクタリング途中であるマーキングで使えるんじゃないか?
何も考えてないのか、考えた結果そうしているの違い。

PHPStan通すためにとりあえずmixed書いちゃう的な。

時間が来てしまったので残りはスルー

Breaking Changesがいくつもあるので気をつけよう。
正式リリース後にマイグレーションガイドが出るはずなので、それを参考にしよう。

MSがPHP8をサポートしなくなるのでWSL2とか使おう。

PHPの最新話題についていくにはPHPWeeklyのメルマガがいいぞ。

エンディング

今後の勉強会の紹介。
9/25 PHP LT JAM 【LT未経験者歓迎】 #phpltjam
10/7 PHPerによるPHPerのための「PHPのニュースや記事を語り合う」TechCafe

テックブログのご案内。

次回やってほしいことがあったらTwitterで『#PHPTechCafe』で呟いて。

感想

私はこの手のイベントに滅多に参加しないのですが、今回はたまたま別件について調べてたところ目に入って、日程も近かったので試しに参加してみました。
で感想ですが、

たいへんもうしわけないのだが、9割方知ってた。

まあ話題からしてPHP8のことで、大概は自分で調べてたから仕方ないですけどね。

しかし私くらいPHP8を追ってる人なんてそれほど多くないと思うので、普通の人が聴くぶんには十分に面白いイベントだったと思います。
便利になる新機能がたくさん紹介されたので、PHPの今後への期待も膨らんだことでしょう。
また単なる新機能の話だけではなく、どうしてその機能が入ることになったのか、PHPは何から何になろうとしているのか、そういった思想的なところの話も面白かったですね。

総合的には、次回もまた参加してみようかなと考えるくらいにはよかったと思います。

この画像ファイルは正しいJavaScriptファイルです

$
0
0

以下はSebastian Stamm ( Twitter / GitHub / Webサイト )による記事、This Image Is Also a Valid Javascript Fileの日本語訳です。

01.gif

This Image Is Also a Valid Javascript File

画像は普通はバイナリファイルであり、Javascriptファイルは基本的にテキストファイルです。
いずれも固有のルールに従わなければなりません。
画像ファイルは、データをエンコードするための具体的なフォーマット形式が決まっています。
Javascriptファイルは、実行するためには特定の構文に従わなければなりません。
ところでふと気になりました。
Javascriptとして実行可能な有効な構文を持っている画像ファイルを作成することはできるでしょうか?

ここより先を見る前に、実験の結果を以下のサンドボックスで確認してみることをお勧めします。

https://codesandbox.io/s/executable-gif-8yq0j?file=/index.html

自身で画像をダウンロードして確認してみたければ、こちらからダウンロードできます。

https://executable-gif.glitch.me/image.gif

Choosing the Right Image Type

残念ながら画像には大量のバイナリデータが含まれているため、そのままJavascriptとして解釈しようとするとエラーになります。
そこで私がまず考えたのは、次のようなものでした。
以下のように、全ての画像データをコメントに入れたらよいのではないかということです。

/*ALL OF THE BINARY IMAGE DATA*/

これは有効なJavascriptファイルになります。
しかし、画像ファイルは特定のバイト列で開始される必要があります。
たとえばPNGファイルの先頭は常に89 50 4E 47 0D 0A 1A 0Aです。
最初が/*で始まっていたら、それは有効な画像ファイルではありません。

このヘッダを見ていて次のアイデアを思いつきました。
バイト列を変数名にして、バイナリは文字列として代入できればよいのではないかということです。

PNG=`ALL OF THE BINARY IMAGE DATA`;

バイナリデータには改行コードが含まれている可能性があり、改行コードの扱いはテンプレートリテラルのほうが優れているので、通常の"'ではなくテンプレートリテラルを用いることにしました。

残念ながら、ほとんどの画像ファイルはヘッダ部のバイト列に変数名として許されない文字を含んでいます。
しかし、それが可能な画像フォーマットをひとつ発見しました。
GIFです。
GIFファイルのヘッダは47 49 46 38 39 61で、これは文字列GIF89aをASCIIで綴ったものであり、すなわち完全に合法ということです。

Choosing the Right Image Dimensions

有効な変数名で始めることができる画像フォーマットを見つけたので、次はそこに等号=とバックティック`が必要です。
従って、ここではファイルの次の4バイトを3D 09 60 04とすることにします。

02.png

GIFフォーマットでは、ヘッダの次の4バイトは画像のサイズを表します。
この中に等号の3Dとバックティックの60を埋め込まなければなりません。
GIFはリトルエンディアンであるため、画像サイズにはそれぞれ2バイト目が大きな影響を与えます。
何万ピクセルもある画像にならないように、3D60は下位バイトに格納することにしました。

画像サイズの残ったバイトには、GIF89a= `という有効文字列を残すために空白文字を入れます。
最も画像幅を小さくできる有効な空白文字は水平タブ09であり、従って画像幅は3D 09、リトルエンディアンでは2365になります。
思ったよりは大きいですが、まだ妥当なサイズです。

画像の高さについては、ちょうどよいアスペクト比になりそうなものを選ぶことができます。
今回は04を選択したので、画像の高さは60 04、すなわち1120です。

Getting our own script in there

このJavascriptは今のところ何もしません。
グローバル変数GIF89aに文字列を代入しているだけです。
せっかくなので何か面白いことがおこるようにしたいですよね。
しかし、GIFファイル内のほとんどは画像をエンコードするためのデータなので、そこにJavascriptを入れようとすると、それは単に壊れた画像になってしまうでしょう。
ところで何故か、GIFフォーマットにはComment Extensionという機能が含まれています。
これはGIFデコーダで解釈されないメタデータを保存する場所であり、すなわちJavascriptのロジックを配置するのに最適な場所ということです。

Comment ExtensionはColor Tableのすぐ後ろに配置されています。
ここには任意の文字列を入れることができるため、変数GIF89aの文字列を閉じることも簡単です。
そこで任意のJavascriptを記述し、最後にコメント開始を入れることで、残りの画像の部分をJavascriptパーサに解釈されないようにします。

全体として、ファイルの中身は以下のようになります。

GIF89a=` BINARY COLOR TABLE DATA ... COMMENT BLOCK:

`;alert("Javascript!");/*

REST OF THE IMAGE */

ただし、少しばかり制限があります。
Comment Extensionは複数のサブブロックで構成する必要があり、ブロックごとの最大サイズは255です。
そしてサブブロックの終わりには、次のサブブロックの長さを表すバイトを記述しなければなりません。
従って、大きなスクリプトを入れたい場合は、以下のように小さな部品に分解していく必要があります。

alert('Javascript');/*0x4A*/console.log('another subblock');/*0x1F*/...

コメント中に書かれているhexcodeは、次のサブブロックの長さを表しています。
これはJavascriptには不要ですが、GIFフォーマットとしては必要です。
従って、Javascriptの邪魔にならないようにJavascriptコメント中に書かなければなりません。
この問題を解決するため、スクリプトをチャンクに分けるための小さなスクリプトを書きました。

https://gist.github.com/SebastianStamm/c2433819cb9e2e5af84df0904aa43cb8

Cleaning up the Binary

基本的な構造が分かったので、次は画像データのバイナリがJavascript構文を壊さないようにする必要があります。
上で説明したように、ファイルには3つのセクションがあります。
ひとつめは変数GIF89aへの代入部分、ふたつめがJavascriptコード、最後は複数行コメントです。

まずは最初の変数代入部分を見てみましょう。

GIF89a=` BINARY DATA `;

バイナリデータに`、もしくは${が入っていたら、そこでテンプレート文字列が終了したり無効な式が生成されてしまうため困ったことになります。
この修正はとても簡単です。
すなわち、バイナリを変更するだけです。
たとえばバイナリ中にASCIIコード60があったら、それを61、すなわち文字aに変更します。
ここはカラーパレットの定義部分のデータを変更することになるため、結果として一部のカラーコードが、たとえば#286048から#286148へと微妙に変化することになります。
しかし、この違いに誰かが気付く可能性は非常に低いでしょう。

Fighting the corruption

Javascriptコードの最後に、バイナリデータがJavascriptに影響しないようにコメント開始コードを書きました。

alert("Script done");/*BINARY IMAGE DATA ...

もし画像のバイナリに*/が含まれていたら、そこでコメントが終了してしまい、不正なJavascriptファイルになってしまいます。
従って、ここでも同じように2文字のうちどちらかを変更することで、コメントが終了しないようにすることにします。
しかし、ここは既にエンコードされた画像セクションであるので、単純に書き換えるだけではこのように画像が壊れてしまいます。

03.gif

極端な場合、画像が全く表示されなくなることすらありました。
しかしどの文字を変更するかを慎重に選択することで、画像の破損を最小限に抑えることができました。
幸いなことに、問題になるような*/はほんの少しだけでした。
最終的な画像は、Valid Javascript File文字列の下のほうなどに僅かな破損が見えますが、概ね満足のいく仕上がりになっています。

Ending the File

最後にしなければならない問題は、ファイルの最後にあります。
ファイルの末尾は00 3Bで終わらせなければならないため、最後までコメントで埋めることはできません。
ファイルの終わりであるため、バイナリデータの変更は目に見えるような画像の破損を起こしません。
従って、Javascriptが正常に動くように、コメントブロック終了後に1行コメントを追加しました。

/* BINARY DATA*/// 00 3B

Convincing the Browser to Execute an Image

ここまできて、ついにJavascriptとして有効な画像ファイルが完成しました。
しかし、最後にまたひとつ問題があります。
この画像をサーバにアップロードし、scriptタグで読み込もうとすると、次のようなエラーが発生する可能性があります。

Refusedtoexecutescriptfrom'http://localhost:8080/image.gif'becauseitsMIMEtype('image/gif')isnotexecutable.

ブラウザは『こいつは画像ファイルだから実行なんてしないぞ』と拒否します。
大抵の場合、これは適切な動作と言えましょう。
しかし、私はこれを実行したいのです。
ということで、その解決策はそのファイルが画像であることをブラウザに伝えないことです。
そのためだけに、ヘッダ情報を送らず画像ファイルだけを返すサーバを作りました。

ヘッダのMIME typeがなければ、ブラウザはそのファイルの正体が何であるかわからず、コンテキストに即したものを実行します。
すなわち、<img>タグでは画像として表示し、<script>タグではJavascriptとして実行します。

But ... why?

なんのため?
それは、私がまだ解明できていないことです。

これを作るのは精神的によい挑戦でした。
もし、これが実際に役立つようなシナリオを思いついたら、ぜひ教えてください!

コメント欄

「面白かった!」
「🤯」
「こいつはクールだ……ぜ?🤔」
「画像ファイルにトラッカーを仕込むとか?もうひとつ思いついたのは、単純にハッキングとか。」「画像に情報を仕込む技術は既にある。ステガノグラフィでぐぐれ。」
「セキュリティへの影響について考えてる。imgタグに入ってる画像をブラウザが勝手にJavaScriptとして実行したら最悪だ。」「imgタグはJavaSctiptと解釈されないから問題ない。」

感想

img srcscript srcどちらで読み込んでも正しく動作する、面白ファイルが出来上がりました。

手法自体はコメントにバイナリを埋め込むというもので、古来より幾度となく行われているものですが、今回はJavaScriptを動くようにしたうえで画像としても破綻無く仕上げているところが面白いですね。

この手法の実用的な例というとCutwailがありますが、これは別に正しい画像である必要はないからちょっと違うかな。

今はセキュリティ上できないようになっているはずですが、この手法を突き詰めたらいずれ、
・Webサイトにアクセスしたら、一枚の画像ファイルが落ちてきた
・画像の表示が終わったと思ったら何故かHTMLになっていた
みたいなこともできるかもしれませんね。

そんなことをやって何のメリットがあるのかさっぱりわかりませんが。

SJIS-macに変換したはずなのにSJIS-winになる

$
0
0

SJIS-macに変換したはずなのにSJIS-winになる

StackOverflowにmb_detect_encoding does not detect SJIS-mac?という投稿がありました。

01.png

ということらしいので、UTF-8からSJIS-macに変換してみましょう。

$utf8Str="❶❷❸❹❺";$sjisStr=mb_convert_encoding($utf8Str,'SJIS-mac');echo(mb_detect_encoding($sjisStr,['UTF-8','SJIS-mac','SJIS-win','SJIS']));// SJIS-win ←

SJIS-macに変換したはずなのに、何故かSJIS-winと判定されてしまいます。

そもそもSJIS-macってなんだよって話ですが、単にMacJapaneseのエイリアスです。
従ってMacJapaneseと書いても同じく、正しく誤判定されます。

そしてコメント欄This is a bug in PHP's mbstring extension『mbstringエクステンションのバグじゃよ』という人が現れています。
間違ったコードを書いたときに自分のせいではなく言語・ライブラリのせいにする人はよくいますが、この人の場合は適当言ってるわけではなく本当にバグです。

https://github.com/php/php-src/pull/6052

コメント主のalexdowadは先日、PHPソースにプルリクを送り付けました。
中身はmbstringエクステンションに関するもので、バグ修正、不要なコードの削除、トリッキーなコードのリファクタリング、高速化、テストカバレッジ上昇など多くの改善が含まれています。

これがなかなか大規模で、91コミット5万行以上のごつい変更となっています。
さすがに大半はテスト用の変換テーブルみたいですが、それでも実コードの修正も数千行は入っています。

mbstringなので日本語に影響するかもしれないということで少し覗いてみました。

https://github.com/php/php-src/pull/6052#issue-475811327

このプルリクはmbstringの改善を目的としていいる。

・不要なコードを削除。
・曖昧なコードを読みやすくする。
・残ってしまったトリッキーなコードにはコメントを追加する。
・全てを高速化する。
・バグの修正。
・テストカバレッジの向上。
・一貫性を向上させるため、セマンティクスを調整した。

もしかしたらパフォーマンスのためにあえてそのように書かれているのではないかと考えていた部分もあったが、実際はそうでもなかった。
リファクタリングすることは、パフォーマンスのためにも良いことだった。
性能チェックのために591のベンチマークを走らせた。
幾つか性能劣化が見つかったが、それも修正した。
最適化の余地はまだまだ大量に残されているので、いずれそれらも改善する予定である。

https://github.com/php/php-src/pull/6052/commits/0880fc00094f363dcaac6762f2ec94a02e380baf

mb_check_encodingを最適化したよ
これによってmb_check_encodingが50%高速化されます。

https://github.com/php/php-src/pull/6052/commits/6197b8e7a2ec81e63e1079812741dccdab0b6417

HTMLの数値エンティティ変換を最適化したよ。
これによってmb_encode_numericentityが10%高速化されます。

https://github.com/php/php-src/pull/6052/commits/e3d5a05a97c5ef8d60489ca3491930bf75d79647

mb_str_splitの本体をmbfl_str_splitに移したよ。あとバグも修正した。

https://github.com/php/php-src/pull/6052/commits/4d9999674ec9fc298e3fe26e7445f24c50575792

ISO-8859-xエンコーディングの変換テーブルとテストスイートを追加したよ。

ちなみにISO-8859-3(Latin-3)ISO-8859-6 (Latin/Arabic)ISO-8859-7 (Latin/Greek)ISO-8859-8 (Latin/Hebrew)ISO-8859-16 (Latin-10)が追加されてる。

https://github.com/php/php-src/pull/6052/commits/322bf79b92ab98d54fde1b30c9ebb633b71d8391

MBFL_CHP_{CTL,DIGIT,UALPHA,LALPHA,MSPECIAL}使ってないから削除したよ。

https://github.com/php/php-src/pull/6052/commits/5b0984a5a513cdc7b7a76986c7dc391c90725d3f

意味のないポインタ参照を削除したよ。

(constchar*(*)[])&mbfl_language_uni_aliases,

というか何これ。

https://github.com/php/php-src/pull/6052/commits/136a03103e8566fa89189c8dc65cb6bffe5c92a4

IMAPのために、UTF-7の改良版Modified UTF-7を追加したよ。

https://github.com/php/php-src/pull/6052/commits/fb98c004cb71fe0707ce35bc46c9cfb8465b9891

Shift-JIS-2004を追加したよ。

https://github.com/php/php-src/pull/6052/commits/2621a78593d8bc1ef5131521b56ca1d98a86776e

ISO-2022-JP-2004の識別を厳密にしたよ。

https://github.com/php/php-src/pull/6052/commits/26204445a7e8b08ee5a0dbbbe247e970e75df8dc

mb_convert_kanaが存在しない変換オプションに例外を出すようにしたよ。

https://github.com/php/php-src/pull/6052

動作検証のためにテストコードを追加したら、既存コードに多くのバグを発見してしまったよ。

https://github.com/php/php-src/pull/6052#issuecomment-695835716

mb_strposに面白い問題を発見したよ。
mb_strposには$needleの長さ > $haystackの長さだったら即falseにするって処理が入ってるんだけど、同じ文字を複数の形式で表すことができるエンコーディングもあって、その場合$needleの長さ > $haystackの長さになることがあるんだよね。

他にも大量のコミットが存在します。

Nikita

レビュアーとしてNikitaが現れる
いや本当、何処に行ってもこの人がいるな。

mbstringは元々libmbflというライブラリを使ってたんだけど、これはもう長いことメンテされていない。
なのでPHP7.3/7.4でこのあたりに大幅なメスを入れたんだけど、まだ概念的には別のライブラリとして扱っている。
このプルリクではlibmbflを捨て、直接Zend APIとして使用するようにしてるけど、それによるデメリットはないので今後はそうする方向がいいと思う。

あとヘッダ部分の書式、何処から来てるのかはわからないけど3スペースのインデントが使われているけど、これはそのままのほうがいいだろう。

ものすごい勢いで突っ込み・コミットを入れているものの、全体的には反対ではなさそうです。
おそらくこの変更はそのうちマージされそうな雰囲気です。

感想

いやあ、この人やばいね。
日本人でも全然わかってない人も多い(私とか)Shift_JIS-2004やらISO-2022-JP-2004やらも的確に対応して大量のテストを追加してさらに最適化・高速化まで成し遂げてしまう。
すごすぎる熱量と能力です。

さらにこのプルリク、(part 1)とか書いてありますからね。
パート2では今回あまり手の入っていないmb_convert_encodingあたりにも最適化が入りそうですね。

しかし、mbstringの主戦場である日本や中国からのコントリビュートが全くないのが気になります。
極端な話英語圏ではmbstringがなくてもなんとかなりますから、切り捨てられないためにも日本からも有識者が参加して存在感を出してもらいたいところですね。

Viewing all 338 articles
Browse latest View live