Quantcast
Browsing Latest Articles All 67 Live
Mark channel Not-Safe-For-Work? (0 votes)
Are you the publisher? or about this channel.
No ratings yet.
Articles:

LGTMについて思うこと

文字と数値がくっついていて、大きさが似ていて、色合いも同じで、著者名やタグの長さによって配置される位置が異なります。
結果として、ぱっと見で数値が全く読み取れません。

01.png

前は色つきの記号だったので自然にそこで分割することができていたのですが、意味のある文字にしたせいで文字と数値が脳内でくっついてしまい、パースに詰まります。

もっと文字と数値を無意識に分てるようにUIを考えてほしいところです。

これがタグページになると、何故かタグがLGTMの右側に移動してくるため、文字列の間に埋没してしまい益々読みにくくなります。

02.png

個人ページ新着ページになると、LGTMが右端の固定位置に寄るため、他のページに比べるとずっと見やすくなります。

03.png

それでも、かつての👍よりは圧倒的に見難いですけどね。

全体としてデザインがまとまっていればまだマシなのかもしれませんが、見てのとおり何故かページごとに配置がバラバラです。
LGTMの位置を把握するだけでも一苦労です。
Qiitaにはデザインリーダー的な人物はいないのか??

まあここの運営、何度もいいねを消したりしてるので、最終的には全部消したいだけなのかもしれませんけどね。

個人ページの情報量が激減した

この記事を書いた瞬間個人ページのLGTMを左側に持っていきやがった。
あえて視認性を悪くする工夫を盛り込む。

さらに、個人ページのレイアウトが格段に劣化した。
・1ページあたり僅か5件しか記事を表示しなくなった。
・コメント数を潰す。
・自分のLGTM履歴やコメント履歴を確認することができなくなっている。
 ※プロフィール右上にある小さな『…』を押すとそこから見れる。極めてわかりにくい。

04.png

Contributionsページに至っては、上部にあった日々のContribution数履歴は消えるし、記事の累計LGTM数もコメント数も一切何も表示されなっている。

05.png

ほんと情報量を減らす改悪ばっかり実行するよな、ここ。

タグページの動作がまたおかしくなってる

地味に、タグページのブラウザバック動作がまたおかしくなっている。

タグページを表示
を選んで2ページ、3ページ、4ページと進む
・ブラウザバックすると、URLは変わるのに表示内容が変わらずにそのまま。

マイページは正常にブラウザバックする。
Contributionコメントページ新着一覧などは元よりページ遷移型なので正常にブラウザバックする。

一応日本最大の技術サイトでありながら、この有様はいったい何なんですかね?
なんつうか、チーフデザイナー的な人物とテスト管理者的な人物を採用すべきでしょう。
あとリーガルアドバイザーもね。
正式に雇うまでいかなくても、ちょっと食事代出すだけで自発的にやってくれそうな人はいっぱいいると思うのですが。

2000円のガラケーで快適にWebを表示する技術

PWA?SPA?WebAssembly?
うん、そうだね、よかったね。
それで、そのページは携帯で見れるのかい?

以下はAddy Osmani( Twitter / GitHub / Facebook / HP )による記事、Loading web pages fast on a $20 feature phoneの日本語訳です。
ちなみにこの人はGoogleのエンジニアで、Chromeの開発者のひとりです。

Loading web pages fast on a $20 feature phone

ヒント:高速なWeb基盤を構築することが、低価格のフィーチャーフォンにも、最新のハイエンドスマートフォンにも、全ての人々に良い体験を提供します。

Introduction

20~25ドル未満で購入できるフィーチャーフォンは、発展途上国において数億人が使用しているローエンドデバイスです。
それらはスマートフォンの低性能バージョンとも言えます。

低価格フィーチャーフォンは、CPUの性能が低く(ハイエンドスマホより6倍以上遅い)、RAMが少なく(通常4GB未満で、256-512MBも普通)、ストレージも少なく(4G)、そして大抵はタッチスクリーンもついておらず、ナビゲーションにはキーパッドや十字キーなどを使います。
たとえば以下のようなものです。
01.jpg

これらのデバイスは、ハイエンドスマホのようにリッチJavaScriptやメディアを処理することができません。
そのため、これらに送信するペイロードには特別な注意が必要です。

02.png

上記図はGeekbenchによる、2019年上半期に最も売れたスマートフォンのCPUベンチマークです。
人気のフィーチャーフォンであるNokia 3110のパフォーマンスを強調表示しています。

JavaScriptはシングルコアのパフォーマンスが重要であり(Webの他の技術と違い、JavaScriptは本質的にシングルスレッドです)、その性能はCPU性能に依存します
発展途上国について考えるときは、このデバイス特性に留意する必要があることを意味します。

この記事では、デバイスの性能にかかわらず誰もが利用できるサイトを構築することで、これらの問題に対処していきます。

Background

フィーチャーフォンは、スマートフォンが登場する前、2000年代半ばまで人気のあった端末であることを覚えている人もいるかもしれませんし、覚えていない人もいるかもしれません。
タッチスクリーンではなく小さなキーパッドを備えていて、電話、テキストメッセージ、テキスト中心のシンプルなWebブラウジングなど、かなり基本的な機能でできていました。
スマートフォンの登場後、これらの電話は先進国ではあまり見られなくなりました。

発展途上国では、誰もが4Gネットワークの無制限データプランでスマートフォンを利用できるわけではありません。
この市場は、スマートフォンから最低限の機能を抽出し、ハードウェア性能と価格の妥協の産物、スマートフィーチャーフォンによって成り立っています。

03.png

スマートフィーチャーフォン市場は2017年から伸びに伸びており、2019年には世界で4億台ものスマートフィーチャーフォンが販売されると想定されています

フィーチャーフォンの成長は、3110や8110(Paul Kinlanがデバッグガイドを公開しています)といった往年の名機をNokiaが復活させたことに支えられています。
インドでは、Reliance Jioが外出先でWebにアクセスするための、安価で最新のフィーチャーフォンを提供しています。
JiojはLinuxベースのフィーチャーフォン向けOSであるKaiOSを推進してきました。

フィーチャーフォン市場の成長により、効率的に動作するサイトが必要となってきており、そのためには注意すべき制約がいくつも存在します。

04.jpg

これはGoogle Images LiteとFacebook mBasicのサンプルです。
これらはいずれもフィーチャーフォンで快適に動作するようつくられており、クライアント側のスクリプト依存は最小限です。
ゲームであるProxxはスクリプトに多く依存しますが、フィーチャーフォンでも高速にロードするため積極的なコード分割設計を行っています。

Feature phone constraints

発展途上国のユーザは、3つの要因によって制限されています。
・低価格かつ高性能のデバイスはほとんどない
・高品質なネットワークがない
・手頃な価格のモバイルデータ通信がない

フィーチャーフォン向けサイトが必要であるならば、これらの制約に注意してください。

  1. ハードウェア
    フィーチャーフォンは大抵、低速(1.1GHz程度)のシングル・デュアルコアCPUと、512MB未満のRAMでできています。
    この制約が意味するところにおいては、8コアCPUと4GBのRAMでできているiPhone XSと比較してみるとよいでしょう。

  2. 通信量
    データプランは安くなりつつありますが、フィーチャーフォンが人気のある地域では依然として大きな制限があります。
    ページが高速に読み込まれ、費用が多くならないように、できるかぎりペイロードを小さくしましょう。

  3. スクリーンサイズ
    フィーチャーフォンの画面サイズは通常、スマートフォンのそれよりはるかに小さいです。
    2.4インチの大きさで、ほんの少ししか情報を出すことができません。
    必要なリソースをビューポート内のコンテンツにできるだけ早くロードすることを第一に考えてください。

  4. タッチスクリーン
    タッチスクリーンがないので、画面上の各機能、アクションボタン、リンクへはキーパッドから簡単にアクセスできる必要があります。
    必要以上にショートカットキーを埋め込む必要はありません。

  5. キーパッド
    フィーチャーフォンのキーパッドは、我々が使い慣れているQWERTYキーボードとは全く異なっています。
    およそ15個のボタンでできていて、多くの文字はボタンを何度も押さなければなりません。
    従って、タイピング量をできるだけ減らすUXを考える必要があります。

日本においても、データプランの上限がユーザエクスペリエンスに影響を与える可能性があります。

Development Guidelines

以下のTIPSは、フィーチャーフォン向けWebサイトに高速なエクスペリエンスを提供するのに役立つでしょう。
全体的に、ユーザが要求したもの以外のために待たせないでください。
JavaScriptのダウンロード時間と実行時間を可能な限り抑えてください。

Set performance budgets for your initial payloads

良好なパフォーマンスを確保するために開発チームが従わなければならない制限を決める、Performance budgetsという考え方があります。
超過することを許さない制限です。
開発を始める前に定量化可能なメトリックを定義し、新しい機能を追加しても総量が制限を超えないようにします。

Performance budgetsの例としては、JavaScriptのバンドルサイズ、画像のサイズ、HTTPリクエスト数などがあります。
ユーザエクスペリエンスを図るための指標として、First Contentful PaintLargest Contentful PaintFirst Input Delayなどを採用可能です。
対象ユーザに応じて、これらのメトリックごとに閾値を決めていきましょう。

Performance budgetsはアプリケーションロジック、vendorやcommonsのバンドル、そしてその他の項目ごとに設定可能です。
Lighthouseを使ったビルドプロセスや、継続的インテグレーションにおいて強制させることができます。

PRPL-30 - a JavaScript budget for feature phones

Chrome開発チームは、低速ネットワーク上のローエンドデバイスでもなるべく早く操作可能にするため、コード量を細かく制御するPRPLパターンを推奨しています。

PRPLは、ページを表示するために必要な最小限のスクリプトのみをプリロードし、それ以外はあとから遅延ロードし、そしてService Workerで今後のナビゲーション用のデータをキャッシュしておくことを勧めています。

PRPL-50は、イニシャルリソースを50KBに制限するという、Performance budgetsを発展させた考え方です。
フィーチャーフォンはCPUによる制約も受けるため、JavaScriptにさらに厳しい制限を課す必要があります。
フィーチャーフォンを対象とするWebサイトは、PRPL-30(30キロバイト)がよいと考えています。

05.png

この仮定においては、SSLネゴシエーションが終わった後、適切なCDNからレスポンスの最初のバイトが返ってくるまでの時間はおよそ2秒です。
従って、初回のペイロードがダウンロードされ、レンダリングされ、ユーザが画面の操作をできるまでの時間は約3秒となります。
すなわち、JavaScript中心のWebサイトの場合は、minifyとgzip圧縮を行った後のバンドルサイズを30キロバイト未満に縮小しなければならないということです。

06.jpg

待って待って。
30キロバイトだって?
ふざけてんの?
Reactのライブラリすら入れることができないんですけど!

厳しい制約のあるデバイス向けのサイトを構築するのであれば、ユーザエクスペリエンスを追求するために難しいトレードオフを行う必要があります。
たとえばReactをサーバ側にしか使わない、あるいはアプリケーションロジックを最小限にして遅延ロードを行わせる、Preactのようなソリューションを使う、などです。
これらのトレードオフについては後で解説します。

PRPL-30を守ることのできるアプリケーションの例としては、たとえば25KBのファイルサイズと5秒未満のインタラクション時間を持つProxxです。
独自の指標を目的とする際は、パフォーマンス予測ツールが役に立つでしょう。

遅延ロードするファイルのサイズも35KB未満が目安です。
30-35KBのチャンクサイズは、V8スクリプトストリーミングが並列処理を行うのに十分な大きさです。

Be Frugal with JavaScript

tl;dr:可能なかぎりスクリプトへの依存を最小限に抑え、静的レンダリングもしくはサーバサイドレンダリングを採用する。
クライアント側レンダリングやハイブリッドレンダリングが必須であれば、必要最小限のスクリプトのみをできるだけ少ない回数リクエストする。
プログレッシブリハイドレーションのようなテクニックも検討する。

07.png

JS is the #1 bottleneck on feature phones

フィーチャーフォン向けにインタラクティブな体験を提供する場合、JavaScriptが最大のボトルネックになる可能性が高いことに気を付けてください。
ページをレンダリングする技術の選択によって、ユーザが実際にページを操作できるようになるための時間が遅くなる可能性があるため、この選定は重要です。
サーバサイドレンダリング静的レンダリングを選んだ場合は、インタラクティブなペイロードはできるかぎり小さくしてください。

08.png

JavaScriptにはダウンロード時間および実行時間という、ふたつの大きなコストがあります。
ダウンロード時間は遅いネットワーク(3Gコネクションなど)によって伸び、遅いCPUは実行時間に遅延をもたらします。
下の図は、人気はあるが重いJavaScriptのあるサイトについて、CPUの種類による処理速度の違いを可視化したものです。
ハイエンドのスマートフォンに比べて、ローエンドのスマートフォンでは6倍もの実行時間がかかっていることがわかります。

09.png

これは即ち、レンダリングやインタラクティブ性を大きなJavaScriptバンドルに依存しているのであれば、フィーチャーフォンではユーザがUIを操作可能になるまで30から60秒待たされることすらあるということを意味します。

JavaScriptに必要なダウンロード時間と処理時間を最小限に収めるため、開発者はそれらのリソースをできるかぎり節約する必要があり、ユーザが必要とする可能性のあるルートやコンポーネントのためのJavaScriptだけを、それらが必要になったときにロードしなければなりません。

Keep interactive payloads lean

ペイロードを無駄にしないためにも、以下のことを守るべきです。

・画面外にあるコンポーネントやリソース、あるいはファーストビュー内でもクリティカルではないコンテンツについては、できるかぎり遅延ロードします。
・JavaScriptのコードを分割し、ファーストビューで必要なものだけを最初にロードするようにします。これによりダウンロードするスクリプトの量が減り、ページの読み込みが早くなります。
・バンドルから未使用コードを削除し、できるかぎり無駄のない状態にします。そのためにはバンドルを分析し、使用されていないライブラリや一部しか使われていないライブラリを差し替える必要があります。また、最初に使わないライブラリは遅延ロードさせる必要があります。
差分ロードを用いて、必要な機能のみをブラウザに提供するようにし、過剰なトランスパイルや過剰なポリフィルを避ける必要があります。モダンブラウザに送られるレガシーコードの量を減らすことで、読み込みのパフォーマンスを上げることができます。
・レンダリングや、ファーストビューのUIのためにJavaScriptを使っているのであれば、それらをプリロードしてください。以下のように記述することでそれが重要であることをブラウザに伝えることができ、ブラウザはできるだけ早くロードします。

<linkrel="preload"as="script"href="critical.js">

Choose your stack wisely

10.png

サードパーティ製ライブラリは開発を高速化し、複雑なタスクを容易に実現することに役立ちますが、その対価として重くなりがちです。
フィーチャーフォン向けに開発する場合は使用に注意しなければなりません。
以下のガイダンスを参考にしてください。

・フィーチャーフォンはリソースに大きな制約があるため、可能ならばJavaScriptフレームワークの使用そのものを避けるか、できるかぎり制限してください。
JavaScriptフレームワークは多大なオーバーヘッドを要求します。
Reactを使うのであればサーバサイドレンダリングするか、あるいはビルド時にPreact compatを使ってPreactに入れ替えて30キロバイト削減する、などの代替を検討してください。
Sveltelit-html、そしてVanilla JSなどはバンドルを軽くするための良い選択肢です。

・サードパーティライブラリへの依存をできるかぎり減らし、初期ロードに必要なバンドルサイズを削減してください。たとえばMoment.jsのかわりにdate-fnsluxonを使うなどです。bundlephobia.comなどでサイズのチェックができます。

・状態管理のためにReduxやStoreを使う場合は厳重な注意が必要です。
StateがしばしばHTMLにインライン展開され、レスポンスサイズが非常に大きくなってしまうことがあります。

Adapt to avoid loading heavy resources on slow connections

ヒント:このトピックの詳細については、adaptive loading - improving web performance on low-end devicesを参照してください。

11.png

Adaptive Loadingとは、提供するリソースをeffectiveTypeに基づいて変更する技術で、ブラウザからはNetwork Information APIで利用可能です。
Adaptive servingを使うことで、低速なユーザにも低機能であってもひとまずのエクスペリエンスを提供することが可能になります。

console.log(navigator.connection.effectiveType);// 3G

注意:たとえeffectiveTypeが"4G"であっても、スタバやカンファレンス会場のWifiのように実速度は低速である可能性は十分にあります。

Adaptive servingを使用すべき具体的な例としては、製品紹介ページなどがあります。
接続速度の遅いユーザには圧縮された商品画像だけを、高速なユーザには高品質な画像とJavaScriptによる強化された演出(画像のズームインや、別製品との比較を表示する機能など)を提供したりすることができるでしょう。

フィーチャーフォンにおいては、低速な回線が必ずしも最大の障害であるとは限りません。
遅いCPUや少ないメモリは、回線種別より多くの影響をユーザ体験に与えます。
Client Hintsは、CPUの性能にこそアクセスできないものの、デバイスのメモリ、Viewportの幅、ピクセルレート、ネットワーク情報、そしてその他の情報を集めることで、より詳細なサービス戦略を立てるために使うことができます。

Be respectful of users data plans with the Save-Data header

Android版Chromeにはライトモード(Data Saver)と呼ばれる機能があり、通信量を気にするユーザは、ブラウザがリソースを最適化してページの読み込み速度を向上させるオプションを選ぶことができます。
最適化には、画像を圧縮したり、重要でないリソースを後回しにしたり、ページをサーバ側でレンダリングすることなどが含まれています。
詳しくはChrome Lite Pagesをご覧ください。

対応しているブラウザでユーザがデータ節約モードを選択すると、あらゆるHTTP/HTTPSリクエストにSave-Dataリクエストヘッダ
アプリケーション開発者は、このヘッダがあれば重い機能をオフにするなど、データ節約モードをオンにしているユーザに最適化された体験を提供することができます。
JavaScriptでは以下のスニペットで確認可能です。

if("connection"innavigator){if(navigator.connection.saveData===true){// Implement data saving operations here.}}

12.jpg

注意:お使いのフィーチャーフォンはChromeをサポートしているかもしれませんが、お使いのChromeがライトモードをサポートしているとは限りません。

Offload costly app logic and state handling to Web Workers

SurmaによるTechniques to make a web app load fast, even on a feature phoneを読んでください。
素晴らしい投稿です。

ブラウザのメインスレッドでは、JavaScript以外にもページレイアウト、ピクセルの描画、ユーザインタラクションの追跡など多くの機能が動いています。
複雑で時間のかかるJavaScriptは、これら他のタスクをブロックしてしまう可能性があります。

Web Workersは、メインスレッドをブロックせずにバックグラウンドでJavaScriptを実行できる技術です。
複雑なアプリロジックや状態管理サービスなど、コストのかかるJavaScriptのオーバーヘッドをメインスレッドから切り離すことができます。
メインスレッドとワーカースレッドは、postMessage()onmessageハンドラでやりとりできます。
postMessageを使って、任意の値やオブジェクトを含むひとつの値を送り付けることができます。
実装の困難はComlinkのようなライブラリで軽減することができます。

ワーカースレッドを使った場合と使わなかった場合でProxxの差を調べた、Surmaのケーススタディは読みごたえがあります。
Nokia2(RAM1GB、1.3GHzクアッドコア)では、ワーカースレッドを使わなければ6.6秒もの間アプリがフリーズしました。
しかし、ワーカースレッドを使った場合は、応答するまでの時間は48ミリ秒でした。
CPUをごりごり使う処理を書いているのであれば、それをワーカースレッドに移植する価値は十分にあると言っていいでしょう。

Optimize Images

画像は多くのデータ量を消費します。
低価格デバイスでは特に、デコードにも時間がかかります。
従ってフィーチャーフォンに画像を配信する際は、適切に画像のサイズと圧縮方法を選ぶことが大事です。

Imageminのようなツールを使って、品質を下げずに画像のサイズを削減します。
アニメーションGIFは動画に変換しましょう。ロードがはるかに速くなります。しかしその前に、ローエンドデバイスがそんなに重いメディアファイルを必要としているか考えてください。
・可能なら画像を遅延ロードしますが、遅延ロードさせるJavaScriptライブラリが重くては意味がありません。ネイティブのloading属性が役立つでしょう。
・同じ画像を複数のサイズで用意しておき、ユーザのviewportに最も適したものを選択して提供するレスポンシブイメージが役立ちます。
画面に合った画像を提供します。低解像度のローエンドデバイスにはそれなりの画像を送ることで、より高速にデコードを行うことができます。

Detecting screen-size

現在は多くのスマホがQVGAで、解像度は320px * 240pxとなっています。
機能のオンオフやアダプティブローディングを画面サイズによって切り替えたい場合、以下のようなスニペットを使うことができます。

constisFeaturePhone=Math.min(screen.width,screen.height)<=240;

これは、Porxxが採用しているアプローチに似た方法です。

Emulate a feature phone in Chrome DevTools

低価格フィーチャーフォン向けサイトを構築するのであれば、格安端末の実物を入手することを強くお勧めします。

Chrome DevToolsでフィーチャーフォンをエミュレートするのであれば、以下の手順で可能です。

・Chrome DevToolsを開く
・Toggle Device Toolbarを選択
・デバイスのドロップダウンからEdit→Add custom deviceを選択
・名前をKaiOS(もしくは必要な対象名)にする
・幅240、高さ320を指定
・UAをMozilla/5.0 (Mobile; LYF/F90M/LYF-F90M-000-02-23-181217; Android; rv:48.0) Gecko/48.0 Firefox/48.0 KAIOS/2.5 YouTube/1.66.51.Jにする
・保存する
・(オプション)CPU性能をカスタマイズすることもできますが、これはあまり正確ではありません

14.png

Conclusion

何処にいる誰にでも、全てのユーザに楽しい体験を提供することは可能です。
ただし、全てのユーザのハードウェアが同じではないので、その点には注意が必要です。

端末の価格が手頃であるほど、CPUの性能も同等に低い可能性が高まります。
JavaScriptのパフォーマンスがダウンロード時間と実行時間に依存していることを考慮し、適切なエクスペリエンスを提供することを考えてください。

これはスマートフォンでも注意すべき点ですが、フィーチャーフォンではさらに重要になります。

コメント欄

「すごい有意義な投稿だった!」
「よく見たらChromeの中の人じゃん。有益な情報をありがとう」
「Svelteのパフォーマンスが高いと感じていたので言及してくれてうれしい。Preact試してみる!」「SvelteとSapperが大好き!」「Svelteや類似のフレームワークが早く普及してほしいね」
「バンドルサイズを抑える最も簡単な方法?簡単さ、JavaScriptを使わなければいい。」

感想

JavaScriptなんてほぼ動かないからHTML全部べた書きな、CSSもまともに効かないからfontタグを使え、とかそういうレベルの話かと思っていたら全然違っていて、わりと最近のAndroidフィーチャーフォンの話だった。
JavaScriptもCSSもきちんと認識してくれるけど、単に性能が低くてネットワークも遅いから、そういうところでも見れるように出力をカスタマイズしろ、という話ですね。

日本ではローエンドと言われているデバイス、安価な通信回線でも一般的なWebサイトを見るには十分な能力を持っているため、日本向けのWebサイトであれば正直そこまで気にする必要はありません。
しかし、たとえば日本では完全に無名のTranssionは、アフリカに特化したフィーチャーフォンを作って2000円で毎年1億台売っています。
アフリカは次の中国と期待される未知の市場です。
2000円の超ローエンドデバイスでも快適に見られるWebサイト作りが、世界に目を向けるならば必要となってくることでしょう。

ともあれ、ローエンドだろうがハイエンドだろうが無駄にアニメーションとスクロールを要求するサイトは滅ぶべきである。

【PHP8.0】throw文がthrow式になる

throw expressionというRFCが投票中です。

最初のアイデアは2019/12/06のSebastiaan Andewegによるツイート

それに対して2020/03/19にCarusoが反応し、そしてその日のうちにiluuu1994が最初のプルリクを出しました
はえーよ。

throw expression

Introduction

PHPのthrowは文であるため、アロー関数や三項演算子、NULL合体演算子などの式しか許されない場所から例外を投げることができません。
このRFCでは、それらを可能にするためthrow文を式に変更することを提案します。

Proposal

式を記述可能なあらゆるコンテキストでthrowが利用可能になります。
以下は思いついた便利そうな例です。

// アロー関数$callable=fn()=>thrownewException();// nullチェック$value=$nullableValue??thrownewInvalidArgumentException();// trueっぽいチェック$value=$falsableValue?:thrownewInvalidArgumentException();// 空ではない配列チェック$value=!empty($array)?reset($array):thrownewInvalidArgumentException();

他にも、議論の余地のある使用方法があります。
このRFCでは、以下のような記述も許可されています。

// ifを使った方が意図が明確になる$condition&&thrownewException();$condition||thrownewException();$conditionandthrownewException();$conditionorthrownewException();

Operator precedence

throwが式になると、優先順位を決める必要があります。
以下は現時点で有効な書式の例です。

throw$this->createNotFoundException();// こうなるthrow($this->createNotFoundException());// こうではない(throw$this)->createNotFoundException();throwstatic::createNotFoundException();// こうなるthrow(static::createNotFoundException());// こうではない(throwstatic)::createNotFoundException();throw$userIsAuthorized?newForbiddenException():newUnauthorizedException();// こうなるthrow($userIsAuthorized?newForbiddenException():newUnauthorizedException());// こうではない(throw$userIsAuthorized)?newForbiddenException():newUnauthorizedException();throw$maybeNullException??newException();// こうなるthrow($maybeNullException??newException());// こうではない(throw$maybeNullException)??newException();throw$exception=newException();// こうなるthrow($exception=newException());// こうではない(throw$exception)=newException();throw$exception??=newException();// こうなるthrow($exception??=newException());// こうではない(throw$exception)??=newException();throw$condition1&&$condition2?newException1():newException2();// こうなるthrow($condition1&&$condition2?newException1():newException2());// こうではない(throw$condition1)&&$condition2?newException1():newException2();

共通して言えるのは、全てがthrowキーワードより高い優先順位を持つということです。
このため、このRFCではthrowキーワードの優先順位を可能な限り低くすることを提案します。
現在有効なコードは、たとえ直感に反する動作だったとしても、今後も同じ動作をし続けます。
なぜなら、一般的にthrowは最後に使用するべき演算子であり、それ以降に記述した式は評価されないからです。

低い優先順位の唯一の欠点は、短絡評価のために括弧が必須になることです。

$condition||thrownewException('$condition must be truthy')&&$condition2||thrownewException('$condition2 must be truthy');// こうなる$condition||(thrownewException('$condition must be truthy')&&$condition2||(thrownewException('$condition2 must be truthy')));// こうではない$condition||(thrownewException('$condition must be truthy'))&&$condition2||(thrownewException('$condition2 must be truthy'));

もっとも、こんなコードはほぼ使われていないでしょう。

Backward Incompatible Changes

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

Other languages

C#では同じ文法が2017年に実装されました。

このような言語は他にはあまりありません。
ECMAScriptにプロポーザルがありますが、これは同じ問題を抱えているからです。

Proposed PHP Version(s)

PHP8。

投票

投票は2020/04/19まで、2/3の賛成で受理されます。
2020/04/06時点では賛成14、反対1で、受理される見込みです。

過去のML

9年前とか15年前にも同じ発想があったようですが、そのときは立ち消えになりました。
当時とはPHPのおかれた環境やRFCの出し方などがだいぶ異なることと、そしてなにより実物のプルリクがあるというのは大きいでしょう。

感想

ややこしいよね文と式。
全てが式になればいいのに。

というわけで、今後はもっと気軽にthrowすることができるようになります。
それどころかアロー関数で引数によって値を返したり例外Aを出したり例外Bを出したりすることもできちゃいますよ。
まあ正直、throwを出すようなややこしい式をアロー関数に書くんじゃないよと思ったりはするわけですが。

【PHP8.0?】PHPに名前付き引数が実装されるかもしれない

PHPのソースを眺めていたら、先日2020/04/07にNikitaがなんか面白そうなプルリクを出していました。
Named Parametersという2013年に提出されたまま忘れ去られたRFCがあるのですが、その機能を実装したものです。

どういう機能ってこういうのです。

functionhoge($foo,$bar){echo"foo=$foo,  bar=$bar";}hoge(bar=1,foo=2);// foo=2, bar=1

PythonCSharpなんかで実装されてるやつですね。

Nikita本人は機能が幾つも不足しているよと言っているのですが、不足の内容はOpcache対応や引数アンパックといった周辺機能で、基本的な機能は既に実装されているみたいです。

PHP RFC: Named Parameters

State of this RFC

これは名前付き引数についての準備的なRFCです。
このRFCの目的は、次のPHPバージョンで名前付き引数をサポートすべきか、またサポートするときはどのように実装すべきかを確認することです。
ここで解説している構文や動作は最終的なものではなく、詳細を詰めていく必要があります。

このRFCの実装はまだ完全なものではありません。
これは非常に複雑な機能なので、この機能が必要でなかったのに時間をかけて実装したくはありません。

Update 22-05-2014

私は他のことで忙しく、このRFCには取り組めていません。
このRFCはPHP6に間に合うように復活させるつもりです。
それまでの間、未解決の問題に対する議論の結果の一部をここにまとめておきます。

・名前付き引数のコンセンサスはまだ得られていません。
・名前付き引数のアンパックと、名前のない引数のアンパックは...構文に統合されます。アンパックのRFCは既に決定しているため、ここについて選択肢はあまりありません。
・継承時のパラメータ名チェックは強制されません。これはあまりにも大きなBC breakになるとはんだんされました。

主な未解決の実装上の問題は"Patch"セクションにリストアップされています。

What are named arguments?

名前付き引数とは、パラメータの順番ではなくパラメータ名を使って関数に引数を渡す方法です。

// 通常の引数array_fill(0,100,42);// 名前付き引数array_fill(start_index=>0,num=>100,value=>42);

名前付き引数に渡す引数の順番は自由です。
上記例では関数シグネチャと同じ順番で引数を渡していましたが、異なる順番で渡すこともできます。

array_fill(value=>42,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?

名前付き引数の明白な利点の一つは、上のコードサンプルを見ればわかります。
変更したい引数までの間にある引数に対して、それぞれデフォルト値を指定する必要から解放されます。
名前付き引数があれば、変更したい引数だけを直接指定することができます。

これはデフォルト引数のRFCでも可能ですが、名前付き引数を使えば意図がより明白になります。
構文を比較してみてください。

htmlspecialchars($string,default,default,false);// vshtmlspecialchars($string,double_encode=>false);

ひとつめのコードを見ても、たまたまhtmlspecialcharsの引数を丸暗記していないかぎりfalseが何を意味するのかは分からないでしょう。

コードの自己文書化の利点は、オプション引数をスキップしないときにおいても明らかです。

$str->contains("foo",true);// vs$str->contains("foo",caseInsensitive=>true);

名前付き引数を使うことで、新しい方法で関数を使うことができるようにbなります。
引数を順番に並んだリストとしてだけではなく、キーと値のペアのリストとしても扱えるということです。
以下のような使い方が考えられます。

// 現在の構文$db->query('SELECT * from users where firstName = ? AND lastName = ? AND age > ?',$firstName,$lastName,$minAge);// 名前付き引数$db->query('SELECT * from users where firstName = :firstName AND lastName = :lastName AND age > :minAge',firstName=>$firstName,lastName=>$lastName,minAge=>$minAge);

Implementation

Internally

名前付き引数は、他の引数と同じくVM stackを通して渡されます。
これらの引数の違いは、位置引数は常にスタックの先頭に渡されるのに対し、名前付き引数は任意の順番でスタックに挿入することができるということです。
使用されないスタックの位置にはNULLが入り、引数カウントはNULLも数えます。

Errors

位置引数と名前付き引数を混在させることが可能ですが、名前付き引数は位置引数の後に配置しなければなりません。
そうしなければコンパイルエラーが発生します。

strpos(haystack=>"foobar","bar");// Fatal error: Cannot pass positional arguments after named arguments

可変長引数ではない関数に存在しない引数名を渡した場合、致命的エラーが発生します。

strpos(hasytack=>"foobar",needle=>"bar");// Fatal error: Unknown named argument $hasytack

同じ名前の引数を複数回渡した場合、新しい方で古い方が上書きされ、警告が発生します。

functiontest($a,$b){var_dump($a,$b);}test(1,1,a=>2);// 2, 1// Warning: Overwriting already passed parameter 1 ($a)test(a=>1,b=>1,a=>2);// 2, 1// Warning: Overwriting already passed parameter 1 ($a)

Collecting unknown named arguments

可変長引数の...$args構文を使った場合、余った名前付き引数は$argsに集められます。
名前付き引数は常に位置引数より後ろとなり、渡された順番が保持されます。

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

使用例としては前述の$db->query()があります。

これはPythonで**kwargsと呼ばれている機能です。

Unpacking named arguments

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

$params=['needle'=>'bar','haystack'=>'barfoobar','offset'=>3];strpos(...$params);// int(6)

文字列キーを持つ任意の値は、名前付きパラメータとして展開されます。
それ以外のキーは通常の位置引数として扱われます。

位置引数と名前付き引数をひとつの配列にまとめることも可能ですが、その場合でも引数の順番は守らなければなりません。
名前付き引数より後に位置引数が出てきた場合は警告がスローされ、アンパックは中止されます。

func_* and call_user_func_array

名前付き引数を使って、スタックから引数としてNULLが渡ってきた場合、func_*関数の挙動は以下のようになります。

func_num_args()はNULLを含んだ引数の個数を返す。
func_get_arg($n)はデフォルト値を返す。デフォルト値がない場合はNULL。
func_get_args()はデフォルト値を返す。デフォルト値がない場合はNULL。

3関数とも、未定義の引数名は無視します。
func_get_argsは値を返さず、func_num_argsはカウントに含めません。

call_user_func_arrayは名前付き引数をサポートしません。
文字列キーを持つ配列を渡すコードが壊れるからです。

Open questions

Syntax

現在の実装および提案では、名前付き引数について以下2種類の構文をサポートしています。

test(foo=>"oof",bar=>"rab");test("foo"=>"oof","bar"=>"rab");

ふたつめの構文は、引数名が予約語である場合のためにサポートされています。

test(array=>[1,2,3]);// syntax errortest("array"=>[1,2,3]);// works

この構文の選択は恣意的なもので、特に深く考えずに採用しました。
以下に、いくつか代替構文の提案があります(ほとんどはPhil Sturgeonによる提案です)。

// currently implemented:test(foo=>"oof",bar=>"rab");test("foo"=>"oof","bar"=>"rab");// キーワードを使えるtest($foo=>"oof",$bar=>"rab");test(:foo=>"oof",:bar=>"rab");test($foo:"oof",$bar:"rab");// キーワードを使えないtest(foo="oof",bar="rab");test(foo:"oof",bar:"rab");// 既に有効なコードなので不可test($foo="oof",$bar="rab");

どの構文で決定するかは議論次第です。

Collection of unknown named args into ...$opts

現在のRFCでは、位置引数と名前付き引数の両方がまとめて可変長引数の...$optsに入ってきます。
Pythonでは前者を*argsに、後者を**kwargsに入れるというアプローチをとっています。

Pros:PHPでは、Pythonではできない配列と辞書の混在ができます。
Cons:位置引数と名前付き引数を別にすることで、意図がより明確になります。必ずしも両方の引数をサポートしたいとはかぎらず、片方だけを強制したいかもしれません。

どのように扱うのが適切か、意見や議論を歓迎します。

Unpacking named args

引数アンパックについても同じ疑問が出てきます。
...$fooは位置引数と名前付き引数を一緒にできますが、*$foo**$fooに分けるべきでしょうか。

この決定は、可変長引数と同じに揃えるべきでしょう。

Signature validation allows changing parameter names

現在のところ、引数名はシグネチャに含まれていません。
位置引数しか使わない場合、これは合理的です。
引数名は関数の呼び出しに関係ないからです。

名前付き引数はこの動作を変更します。
継承先クラスが引数名を変更した場合、名前付き引数を使った呼び出しは失敗し、LSPに違反します。

interfaceA{publicfunctiontest($foo,$bar);}classBimplementsA{publicfunctiontest($a,$b){}}$obj=newB;// Pass params according to A::test() contract$obj->test(foo=>"foo",bar=>"bar");// ERROR!

名前付き引数が導入された場合、シグネチャの検証において引数名の変更にエラーを出さなければなりません。
通常の場合、インターフェイスと実装クラスの不一致は致命的エラーを発生させますが、名前付き引数において致命的エラーを出すとBC breakが大きくなりすぎてしまいます。
かわりに、より低いエラータイプ(WARNING / NOTICE / STRICT)を出すことを検討します。

これに関する具体的な議論のポイントをひとつ挙げておきます。
PHPは、実行時の動作を変更するini設定を導入する習慣を過去に置いてきました。
従って、この挙動をiniで制御できるようにすることは、私の選択肢にはありません。

Patch

差分がこちらにありますが、このパッチは不完全で、ダーティで、既知のバグがあります。

やるべき作業はまだまだ残っています。

・"Open questions"の結果を実装する。
・内部関数の全てのarginfosをドキュメントと一致するように更新する。現在のarginfo構造体は絶望的に時代遅れで、引数割り当てなどは自動的にできるようにしたい。
・内部関数において引数がスキップされたときに適切に動作するようにする。ほとんどの場合は自動的に動作するはずだが、手動調整が必要になる関数もかなりあるだろう。

感想

2014年とかPHP6とか出てくることからわかるように、このRFCはだいぶ昔に書かれてそのまんまです。
当時作成されたパッチはもはや使い物にならないため、新たにプルリクを作ってきたようです。
パッチにしろその他の内容にしろ、当時のPHPと今のPHPはだいぶ異なったものになっているので、なんにしろRFCのリファインは必要になるでしょう。

このプルリクについても、とりあえず提出されただけで何の展開もありませんし、メーリングリストでの議論も特に進んでいるわけではありません。
従って、このプルリクも今後どうなるかはわからず、再びこのまま忘れ去られるかもしれません。
しかしNikitaのことですから、いきなり完動品のプルリクが送られてきて第一線に躍り出る、なんてことがあっても驚きはないですね。

混沌の時代に実装された関数などでは特に、同じ内容の関数でも引数の順番が異なったりしていて大変なのですが、この機能が実装されたら、そのあたりを楽に処理できるようになります。
また却下されたデフォルト引数のRFCも、この名前付き引数があれば不要になります。
絶対にないといけないというほどでもないですが、存在すれば純粋に便利になる、そんな良い機能だと思います。

あとここからは完全に妄想ですが、このプルリクはジェネリクスへの足掛かりなのではないかと感じています。
Nikitaはどうもジェネリクス大好きっ子みたいですから、このプルリクを元に引数まわりに手を付けて、ついでにジェネリクスまでできるようにちゃったぜみたいなことを考えているのではないでしょうか。

【PHP7.4.5】バグレポ出したらPHP本体に取り込まれた

2020/03/10 23:38

先日なんとなく、bugs.php.netバグレポを提出しました。
中身は前書いた配列要素を削除するとインデックスが残ったり残らなかったりするの記事、というかそこに付いた @crhgさんのコメントそのまんまです。

Description

Copy array.

When arrays next index=2 and max key=2, next key is both 2.
When arrays next index=2 and max key=1, next key is both 2.
When arrays next index=2 and max key=0, next key is 0 or 2.

Test script

$a=[1,2];$b=$a;$a[]=3;$b[]=4;echo"When next index=2 and max key=2, next key is both 2.\n";var_dump($a,$b);// $a = [0=>1, 1=>2, 2=>3]  $b = [0=>1, 1=>2, 2=>4]$a=[1,2];unset($a[1]);$b=$a;$a[]=3;$b[]=4;echo"\nWhen next index=2 and max key=1, next key is both 2. \n";var_dump($a,$b);// $a = [0=>1, 2=>3]  $b = [0=>1, 2=>4]$a=[1,2];unset($a[1],$a[0]);$b=$a;$a[]=3;$b[]=4;echo"\nWhen next index=2 and max key=0, next key is 0 or 2.";var_dump($a,$b);// $a = [0=>3]  $b = [2=>4]  what?$a=[1,2];unset($a[1],$a[0]);$b=$a;$b[]=4;$a[]=3;echo"\nIf reverse variable, result is reverse too.\n";var_dump($a,$b);// $a = [2=>3]  $b = [0=>4]  what??$a=[1,2];unset($a[1],$a[0]);$b=$c=$d=$a;$a[]=3;$b[]=4;$d[]=6;$c[]=5;echo"\nWhen increase variables, only the last one take over key..\n";var_dump($a,$b,$c,$d);// $c's key is 2, others are 0

Expected result

Always same key whth $a and $b.

Actual result

Same key when array is not empty.
Not same key when array is empty.

2020/03/11 00:17

cmb69氏から修正のプルリクが提出されました。
バグレポ提出からの時間、なんと40分。
はえーよ。

ちなみにコミット内容は追加22行変更1行ですが、うち追加22行はテストコードなので、実質的な変更は1行だけです。
変更内容は要素数が0のときもnNextFreeElementを引き継ぐようにするというもので、これまた @crhgさんのコメントのとおりです。

cmb69氏はRFCにはあまり出てこないので一見目立ちませんが、githubbugs.php.netでは見ない日がないんじゃないかってくらい、バグフィックスやソースの整理などで大活躍しているメイン開発者の一人です。

2020/03/11 00:35

Nikitaから物言いがつく。

本当にインデックス継続でいいの?
リセットする方が正しいんじゃないか?
継続することにメリットはある?

などと言いつつApproveしています。ツンデレかよ。

Nikitaは最近最も仕事しているPHP開発者で、そしてガッチガチstrictぺちぱーの急先鋒です。

2020/03/11 17:13

このあたりでmasterにマージされました

2020/04/16

この修正が取り込まれたPHP7.4.5がリリースされました

ChangeLogに記載されました
めでたし。

感想

実際にプルリクを出したりコードが採用されたりしたわけではないので、PHP本体に貢献できた!と堂々と主張できるかというと微妙ですが、自分の提案が影響を与えたかと思うとちょっとだけ嬉しいですね。

バグレポを出そう

Qiitaではどうもプルリクを出してようやく一人前、プルリクを出さずんば開発者に非ず、みたいな風潮がありますが、そこまでやらなくてもバグレポ程度でも役には立ちますよ、という趣旨の記事でした。
みんなもおかしなところを見つけたらバグレポを出しましょう。
もっとも、おかしいな、これはバグかな、って思った原因の99%は自分のコードが間違ってるからなので、確認と見極めが大切です。

まあ、私がプルリクを出さなかったのはひとえに面倒だったからというだけなので、プルリクを出せるようなら出した方がもっといいとは思いますけどね。

バグレポを出すにあたって気を付けること

最低限絶対必要なものは、『再現するコード』です。

今回私のバグレポが速攻で対応されたのも、再現するコードを3v4lで出したからというのが大きいと思います。
バグレポを見てのとおり私の英語力はとってもアレなうえ、"When arrays next index=2 and max key=2, next key is both 2"のところとか数値も間違っているというぐだぐだなバグレポだったのですが、しかし出力が明白なコードを付けたおかげで、意図した問題点は汲み取ってもらえました。

次いで『期待する結果』『実際の結果』も必要です。
これがないと、どう修正すればいいかわかりませんからね。
今回私はAlways same key whth $a and $b.と"揃えろ"だけ書いていて、"〇〇に揃えろ"のように具体的な値を示していません。
これは0にしたほうがいいのか2にしたほうがいいのかわからなかったからです。
いわゆるひとつのお任せします(責任転嫁)というやつです。

あとは『再現環境』です。
今回みたいに3v4lで再現するなら特に気にする必要もないと思いますが、CLIでしか発生しないとか、Windows環境でしか発生しないとか、そういう場合は記載が必要となるでしょう。

このように『再現するコード』『期待する結果』『実際の結果』『再現環境』をきちんと揃えることで、デバッガの人も対応しやすくなります。
その程度当然じゃないかと思うかもしれませんが、意外とできていない人が多いのは某QAサイトとか見ていてもよくわかるとおりです。
さすがにbugs.php.netにはそういう人はあまりいませんが、決して全くいないわけではなく、そしてそういう人の書いたバグレポは数年にわたって放置されます。

そんなわけで、注意点に気をつけつつバグレポをどんどん出しましょう。

【PHP8.0】PHPでアトリビュート/アノテーション/デコレータが書けるようになる

Attributes v2というRFCが投票中です。
投票期間は2020/05/04まで、投票者の2/3の賛成で受理されます。
2020/04/27時点では賛成48反対1で、ほぼ間違いなく可決されます。

Attributes v2

Introduction

このRFCは、クラス/プロパティ/関数/メソッド/引数/定数の宣言に、構造化されたアトリビュートをメタデータとして記述できるようにする提案です。
アトリビュートは、コードの宣言に直接設定ディレクティブを埋め込むことで定義されます。

同じような概念としてJavaのAnnotation、C#/C++/Rust/HackにおけるAttribute、Python/JavascriptにおけるDecoratorが存在します。

これまで、PHPではこのようなメタデータとしては非構造的であるdoc-commentsしか存在しませんでした。
しかしdoc-commentsはただの文字列であり、言語によって解釈されることはありません。
構造化された情報を保持するために、PHPの様々なコミュニティにおいて@ベースの疑似メタデータが考案されてきました。

ユーザランドでの使用例に加え、拡張機能などではコンパイルや構文解析、コード生成、実行時の挙動に影響を与えるようなアトリビュートの使用例も多く存在します。
下のほうに例を示します。

ユーザランドのdoc-commentsが多く使われていることから、この機能がコミュニティから強く求められていることがわかります。

Proposal

Attribute Syntax

アトリビュートは、既存の構文トークンT_SLT_SR、すなわち<<>>を使った特別なフォーマットのテキストです。

アトリビュートは言語内の多くの対象に適用されます。
・関数 (クロージャやアロー関数含む)
・クラス (無名クラス含む)、インターフェイス、トレイト
・クラス定数
・プロパティ
・メソッド
・関数/メソッド引数

以下は例です。

<<ExampleAttribute>>classFoo{<<ExampleAttribute>>publicconstFOO='foo';<<ExampleAttribute>>public$x;<<ExampleAttribute>>publicfunctionfoo(<<ExampleAttribute>>$bar){}}$object=new<<ExampleAttribute>>class(){};<<ExampleAttribute>>functionf1(){}$f2=<<ExampleAttribute>>function(){};$f3=<<ExampleAttribute>>fn()=>1;

アトリビュートはdoc-blockコメントと同様に、それが属する宣言の直前に記載します。
doc-blockコメントとどちらが先かといえば、コメントの前にも後ろにも書くことができます。

<<ExampleAttribute>>/** docblock */<<AnotherExampleAttribute>>functionfoo(){}

関数、クラス、メソッド、プロパティ、パラメータはひとつ以上のアトリビュートを持つことができます。

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

ひとつの対象に同じアトリビュート名を複数回指定することが可能です。

アトリビュートは1行で複数回宣言できます。

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

<<>>は式の接頭辞として使用されており、他言語で使用されているジェネリクスの一般的な構文<T>がもし今後導入されることになったとしても、それがアトリビュート構文と衝突することはありません。

構文についてはこのRFCで最も議論されてきたポイントであり、@:として表される新たな構文トークンT_ATTRIBUTEを導入する代替案も考えられました。
構文の選択はRFCの二次投票となります。

@:WithoutArgument@:SingleArgument(0)@:FewArguments('Hello','World')functionfoo(){}

この構文は、doc-blockコメントで一般的に見られる@を使用します。
欠点としては、空白を許可するとアトリビュートの終端を判別できなくなってしまうので、空白を禁止しなければならないことです。

最も要望されるであろう@[]が使えない理由については、下にあるAlternative Syntaxの議論を参照してください。

Attribute Names Resolve to Classes

アトリビュートは、コンパイル中にその時点でインポートされている全てのシンボルについて解決されます。
これはアトリビュートに名前空間を許容するためであり、別のライブラリやアプリケーションで同じアトリビュートが誤って再利用されるのを避けるためです。

useMy\Attributes\SingleArgument;useMy\Attributes\Another;<<SingleArgument("Hello")>><<Another\SingleArgument("World")>><<\My\Attributes\FewArguments("foo","bar")>>functionfoo(){}

このことはクラスにアトリビュートを宣言する際にも利点があります。
・リフレクションが解析しやすい。後述。
・静的解析ツールによる検証が容易になる。
・IDEがオートコンプリートや引数に対応することができる。

クラスアトリビュートを指定する例は以下のようになります。

namespaceMy\Attributes;usePhpAttribute;<<PhpAttribute>>classSingleArgument{public$value;publicfunction__construct(string$value){$this->value=$value;}}

Compiler and Userland Attributes

このRFCは、2種類のアトリビュートを区別しています。
・コンパイラアトリビュート:コンパイル時に検証される
・ユーザランドアトリビュート:リフレクションで検証される

コンパイラアトリビュートは、PhpCompilerAttributeに属する内部クラスです。

ユーザランドアトリビュートは、PhpAttributeに属するユーザランドのクラスです。

コンパイル時にコンパイラアトリビュートが見つかると、実行エンジンはアトリビュートに紐付けられているバリデーションのコールバックを呼び出します。
たとえばこのパッチにはPhpCompilerAttributeのバリデーションコールバックが含まれており、ユーザがPhpCompilerAttributeを使うのを防いでいます。

#include "zend_attributes.h"
voidzend_attribute_validate_phpcompilerattribute(zval*attribute,inttarget){if(target!=ZEND_ATTRIBUTE_TARGET_CLASS){zend_error(E_COMPILE_ERROR,"The PhpCompilerAttribute can only be used on class declarations and only on internal classes");}else{zend_error(E_COMPILE_ERROR,"The PhpCompilerAttribute can only be used by internal classes, use PhpAttribute instead");}}INIT_CLASS_ENTRY(ce,"PhpCompilerAttribute",NULL);zend_ce_php_compiler_attribute=zend_register_internal_class(&ce);zend_compiler_attribute_register(zend_ce_php_compiler_attribute,zend_attribute_validate_phpcompilerattribute);

引数zvalは渡された全ての引数を含み、targetはアトリビュートが正しく宣言されているかを検証する為の定数です。
ユーザランドクラスはPhpCompilerAttributeを使用することができません。
使おうとするとエラーが発生します。

<?php<<PhpCompilerAttribute>>classMyAttribute{}// Fatal error: The PhpCompilerAttribute can only be used by internal classes, use PhpAttribute instead

アトリビュートをクラスツールにマッピングすることで、エディタやIDEは、アトリビュートの構文やコンテキスト情報を開発者に提供することができます。
この方法の欠点は、コンパイラアトリビュートがユーザランドアトリビュートであると誤って分類されてしまうことです。

Constant Expressions in Attribute Arguments

アトリビュートは定数AST式として評価されますが、これは引数を許可することを意味します。

<<SingleArgument(1+1)>><<FewArguments(PDO::class,PHP_VERSION_ID)>>

この主な使用例は、定数/クラス定数を参照することです。
定数を参照することで、既に定数として存在する情報を再定義する重複を避けることができます。
もうひとつの利点として、ツールやIDEによる静的解析でアトリビュートを検証できるということです。

定数ASTは、リフレクションを通してアクセスする際は値に解決されます。
これはかつて提出されたアトリビュートRFCとは意図的に異なる挙動です。

またパーサは、ビットシフト演算子とアトリビュート構文を区別できます。

<<BitShiftExample(4>>1,4<<1)>>functionfoo(){}

Reflection

ReflectionクラスにgetAttributes()メソッドが追加され、ReflectionAttributeインスタンスの配列を返します。

functionReflectionFunction::getAttributes(string$name=null,int$flags=0):ReflectionAttribute[];functionReflectionClass::getAttributes(string$name=null,int$flags=0):ReflectionAttribute[];functionReflectionProperty::getAttributes(string$name=null,int$flags=0):ReflectionAttribute[];functionReflectionClassConstant::getAttributes(string$name=null,int$flags=0):ReflectionAttribute[];

引数$nameがあれば指定したアトリビュート、もしくはそのサブクラスを含めたものを返します。

$attributes=$reflectionFunction->getAttributes(\My\Attributes\SingleArgument::class);

引数$flagが未指定の場合、getAttributes()メソッドは名前が完全一致したアトリビュートだけを返し、この動作がデフォルトです。
ReflectionAttribute::IS_INSTANCEOFを指定すると、instanceofを通過する全てのアトリビュートを返すようになります。

$attributes=$reflectionFunction->getAttributes(\My\Attributes\MyAbstractAttribute::class,\ReflectionAttribute::IS_INSTANCEOF);

ReflectionAttributeクラスは以下のようになります。

classReflectionAttribute{publicfunctiongetName():stringpublicfunctiongetArguments():arraypublicfunctionnewInstance():object}

アトリビュートの検証はReflectionAttribute::newInstance()でのみ行われるので、実は必ずしもアトリビュート名に対応したクラスを定義する必要はありません。
アトリビュート名と引数は直接ReflectionAttributeから取って来れます。

以下は完全な例です。

namespaceMy\Attributes{<<PhpAttribute>>classSingleArgument{public$argumentValue;publicfunction__construct($argumentValue){$this->argumentValue=$argumentValue;}}}namespace{<<SingleArgument("Hello World")>>classFoo{}$reflectionClass=new\ReflectionClass(Foo::class);$attributes=$reflectionClass->getAttributes();var_dump($attributes[0]->getName());var_dump($attributes[0]->getArguments());var_dump($attributes[0]->newInstance());}/**
string(28) "My\Attributes\SingleArgument"
array(1) {
  [0]=>
  string(11) "Hello World"
}
object(My\Attributes\SingleArgument)#1 (1) {
  ["argumentValue"]=>
  string(11) "Hello World"
}
**/

この使い方では、getAttributes()は決して例外をスローしません。
これにより、異なるライブラリが同じ名前のアトリビュートを定義していた際の問題を回避することができます。

Use Cases

Use Cases for PHP Extensions

アトリビュートの主な使用先は、PHPコアと拡張モジュールになるでしょう。

HashTablesへのアトリビュートは、全てのzend_class_entry/op_array/zend_property_info/zend_class_constantで使用可能です。

PHPコアや拡張モジュールは、ある定義にアトリビュートがあるかどうかチェックしたくなることがあるでしょう。
例としてOpcache JITに対する@jitのチェックなどです。
これは、関数やメソッドを常に最適化するようJITに指示します。

アトリビュートが実装されれば、拡張モジュールでは以下のように書けるようになります。

staticintzend_needs_manual_jit(constzend_op_array*op_array)returnop_array->attributes&&zend_hash_str_exists(op_array->attributes,"opcache\\jit",sizeof("opcache\\jit")-1));

開発者は、doc-commentのかわりにアトリビュートを使うことができます。

useOpcache\Jit;<<Jit>>functionfoo(){}

Other potential core and extensions use cases/ideas

以下はアトリビュートの使用法のアイデアです。
RFCの一部ではないことに注意してください。

関数/メソッドの非推奨。
アトリビュートを持つほぼ全ての言語にこの機能が組み込まれています。
PHPにこれがあれば、クラスやプロパティ、定数を非推奨にすることができます。

// アイデアだよ。RFCの一部ではないよusePhp\Attributes\Deprecated;<<Deprecated("Use bar() instead")>>functionfoo(){}

非推奨アトリビュートは、今のところtrigger_errorを使うことができません。

classFoo{<<Deprecated()>>constBAR='BAR';}echoFoo::BAR;// PHP Deprecated:  Constant Foo::BAR is deprecated in test.php on line 7

Reclassify Engine WarningsSupport Rewinding GeneratorsのRFCのようなレガシー動作を、オプトインで変更します。
Rustが似たような機能を持っています。

// アイデアだよ。RFCの一部ではないよusePhp\Attributes\Deny;usePhp\Attributes\Allow;<<Allow("rewind_generator")>>functionbar(){yield1;}<<Deny("undeclared_variables")>>functionfoo(){echo$foo;// PHP Fatal error:  Uncaught TypeError: Access to undeclared variable $foo}<<Deny("dynamic_properties")>>classFoo{}$foo->bar;// PHP Fatal error:  Uncaught Error: Invalid access to dynamic property Foo::$bar

Rustっぽいマクロの一部は、旧バージョンのPHPでのみPolyfillを読み込んだりするときに便利かもしれません。
ライブラリがOpcacheやpreloadingなどを条件付きで宣言するときに役立つでしょう。

// アイデアだよ。RFCの一部ではないよusePhp\Attributes\ConditionalDeclare;usePhp\Attributes\IgnoreRedeclaration;<<ConditionalDeclare(PHP_VERSION_ID<70000)>>// PHP7.0以上ならASTによって削除される<<IgnoreRedeclaration>>// 重複時はエラーを出さず単に無視するfunctionintdiv(int$numerator,int$divisor){}

最終的には、あるアトリビュートの引数を返すAPIや、全アトリビュートの一覧を返すAPIが含まれる予定です。
これによって拡張機能の作者は、最小限の労力でアトリビュートを使うことができるようになります。
以下は草案です。

/* アトリビュート名から引数一覧を返す */HashTable*zend_attribute_get(HashTable*attributes,char*name,size_tname_len);/* アトリビュートを返す */zval*zend_attribute_all(HashTable*attributes,char*name,size_tname_len);

Userland Use-Case: Declaring Event Listener Hooks on Objects

ユーザランドにおいて、アトリビュートは宣言に対する追加設定を宣言のすぐ傍に置くことができるという利点があります。
以下はSymfonyのEventSubscribersをアトリビュートを使ってリファクタリングする例です。
EventSubscriberInterfaceは、イベントをどのクラスのどのメソッドで処理するかをgetSubscribedEvents()で宣言する必要があります。

// 現在のコードclassRequestSubscriberimplementsEventSubscriberInterface{publicstaticfunctiongetSubscribedEvents():array{return[RequestEvent::class=>'onKernelRequest'];}publicfunctiononKernelRequest(RequestEvent$event){}}// リファクタした<<PhpAttribute>>classListener{public$event;publicfunction__construct(string$event){$this->event=$event;}}classRequestSubscriber{<<Listener(RequestEvent::class)>>publicfunctiononKernelRequest(RequestEvent$event){}}// アトリビュートを使ったイベントディスパッチャclassEventDispatcher{private$listeners=[];publicfunctionaddSubscriber(object$subscriber){$reflection=newReflectionObject($subscriber);foreach($reflection->getMethods()as$method){// Listenerアトリビュートを取得$attributes=$method->getAttributes(Listener::class);foreach($attributesas$listenerAttribute){/** @var $listener Listener */$listener=$listenerAttribute->newInstance();// $listener->eventはcallable$this->listeners[$listener->event][]=[$subscriber,$method->getName()];}}}publicfunctiondispatch($event,$args...){foreach($this->listeners[$event]as$listener){// 呼び出し$listener(...$args);}}}$dispatcher=newEventDispatcher();$dispatcher->addSubscriber(newRequestSubscriber());$dispatcher->dispatch(RequestEvent::class,$payload);

Userland Use-Case: Migrating Doctrine Annotations from Docblocks to Attributes

アトリビュートのRFCが考慮した主要なケースのひとつが、広く普及しているDoctrine Annotationsライブラリからの移行可能性です。

PHPコアがアトリビュートをサポートすることで、ユーザがDoctrine Annotationsからアトリビュートへ移行するための基盤を確保することができます。

このためこのRFCは、名前空間を使ったアトリビュートの操作が主な要件となっています。

Doctrineおよび任意のユーザランドライブラリは、親クラスの名前フィルタを利用して、興味のあるアトリビュートだけを抽出することができます。
提案のリフレクションAPIを使用し、独自のロジックを追加することで、より厳格なアトリビュートの使用を強制することができるようになります。

以下に、Doctrineのアノテーションと、このRFCのアトリビュートで同じことを実装した複雑なオブジェクトの例を示します。

<?phpuseDoctrine\ORM\AttributesasORM;useSymfony\Component\Validator\ConstraintsasAssert;<<ORM\Entity>>/** @ORM\Entity */classUser{/** @ORM\Id @ORM\Column(type="integer"*) @ORM\GeneratedValue */<<ORM\Id>><<ORM\Column("integer")>><<ORM\GeneratedValue>>private$id;/**
     * @ORM\Column(type="string", unique=true)
     * @Assert\Email(message="The email '{{ value }}' is not a valid email.")
     */<<ORM\Column("string",ORM\Column::UNIQUE)>><<Assert\Email(array("message"=>"The email '{{ value }}' is not a valid email."))>>private$email;/**
     * @ORM\Column(type="integer")
     * @Assert\Range(
     *      min = 120,
     *      max = 180,
     *      minMessage = "You must be at least {{ limit }}cm tall to enter",
     *      maxMessage = "You cannot be taller than {{ limit }}cm to enter"
     * )
     */<<Assert\Range(["min"=>120,"max"=>180,"minMessage"=>"You must be at least {{ limit }}cm tall to enter"])>><<ORM\Column(ORM\Column::T_INTEGER)>>protected$height;/**
     * @ORM\ManyToMany(targetEntity="Phonenumber")
     * @ORM\JoinTable(name="users_phonenumbers",
     *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="phonenumber_id", referencedColumnName="id", unique=true)}
     *      )
     */<<ORM\ManyToMany(Phonenumber::class)>><<ORM\JoinTable("users_phonenumbers")>><<ORM\JoinColumn("user_id","id")>><<ORM\InverseJoinColumn("phonenumber_id","id",JoinColumn::UNIQUE)>>private$phonenumbers;}

アトリビュートは名前付きパラメータをサポートしていないため、少し制限があります。
これがアトリビュートが関数呼び出しのような構文を利用している理由ですが、もし名前付きパラメータがサポートされれば、自動的にこのRFCもその恩恵を受けることになります。

ユーザランドのアトリビュートへの移行には、Rectorのようなツールが役立ちます。

Criticism and Alternative Approaches

Alternative Syntax: Why not use @ or [] like other languages?

どうして@[]ではないの?

構文として<<>>を使用する理由は、コードのこの場所でまだ使用できる数少ない構文のひとつであり、その中でも自然に見えるものだからです。
接頭辞演算子としてまだ使われていない他の記号を使うことも可能ですが、実質的に使えそうなものは'%'くらいです。
他に使える記号というのは|=/などです。

@[]は、エラー抑制演算子と配列短縮構文に競合するため使用することができません。
以下のような構文は、現在既に有効なPHPコードです。

[[@SingleArgument("Hello")]]

この構文が配列宣言かアトリビュートかを決定するためには、無制限に先のトークンを調べなければならなくなる可能性があります。

Why not extending Doc Comments?

Doc Commentsをそのまま拡張するのではだめなの?

アトリビュートはDoc Commentsより優れています。

・名前空間により、同じDoc Commentsを使用している複数のライブラリによる競合を防ぎます。
・アトリビュートの存在チェックはo(1)のハッシュキーチェックであり、strstrやDoc Commentsのパースより高性能です。
・アトリビュートをクラスにマッピングすることで、アトリビュートが正しい構文であることを保証し、Doc Commentsの書き間違いによるバグの発生を減らします。
・アノテーションは非常に多くのツールやコミュニティで一般に使われているので大きな需要があります。しかし初心者がコメントと見誤ると混乱を招くことになるでしょう。また/*/**の違いもバグの原因になります。

PHPで既存のDoc Commentsを解析して構造化することは可能かもしれませんが、Doc Commentsの方言ごとに追加のパーサを呼び出す必要があります。
Doc Commentsは正しい文法になっていない可能性があるため、文法エラーの取り扱いを決めなければなりません。
最終的に、これはPHP内部に別の言語が存在するということになりそうです。
この方法は、アトリビュートを導入するより遙かに複雑となり、望ましくありません。

Why not always map attributes to simple arrays instead for simplicity?

シンプルに、アトリビュートは常に配列にマッピングするようにすればよくない?

アトリビュートのクラス名解決が何故重要なのか、その利点を前のセクションで解説しました。
アトリビュートが正しいかどうかの検証は、文法が正しいかどうかを検証できないDoc Commentsよりも大きなメリットがあります。

Why not a stricter solution like Doctrine Annotations?

Doctrine Annotationsのようにより厳密なソリューションは導入しないの?

このRFCは、PHPの基本的なアトリビュート機能のみを提案しています。
一般的なソリューションを全て解決するためには様々なユースケースを考慮しなければなりませんが、大抵の場合はDoctrineほど細かなシステムは必要ありません。

Why are nested attributes not allowed?

アトリビュートのネストが許可されないのは何故?

アトリビュートのネストとは、あるアトリビュートを他のアトリビュートの引数として定義するということを意味します。
これは引数でアトリビュートを宣言できるということなので、意図的に禁止されています。

Naming (attributes or annotations)

この機能の"アトリビュート"という名前は、既に使われているアノテーションとの混同を避けるために付けられました。
これによってDoctrine AnnotationsはPHP7ではDoc Commentsで実装され、PHP8ではアトリビュートで実装されている、といったことがおこります。

Backward Incompatible Changes

なし。

Proposed PHP Version(s)

PHP8.0

RFC Impact

To Core

トークン、ASTノード、zend_class_entry、zend_class_constant、zend_op_array、zend_property_infoの全てにアトリビュートを追加する必要があります。

To SAPIs

なし。

To Existing Extensions

なし。

JITは@JITではなくOpcache\Jitを、@nojitのかわりにOpcache\Nojitを使うことになる予定ですが、まだ未定です。

To Opcache

パッチに含まれており、100%の互換ではない可能性があります。

New Constants

なし。

php.ini Defaults

なし。

Open Issues

なし。

Future Scope

・名前付きパラメータとの統合
・下位互換性を壊すことなく、既存関数を新しい動作で拡張させる。
・関数/メソッドが呼ばれたとき、プロパティ/定数にアクセスしたときに非推奨を通知する<<Deprecated>>
・型付きプロパティとアトリビュートで、JSON/XMLからオブジェクトへの直列化がPHPコアでできるようになる。
<<«SingleArgument("foo"), MultiArgument("bar", "baz")>>を簡単に書けるような短縮構文。

Voting

2020/04/27時点で、導入には賛成48反対1で、ほぼ導入決定です。

構文は<<>>が40人、@:が10人で、<<>>に決まると思われます。

Patches and Tests

https://github.com/beberlei/php-src/pull/2<<>>
https://github.com/kooldev/php-src/pull/2@:

References

他言語でのアトリビュート/アノテーション/デコレータ。

Rust Attributes
C# Attributes
Java Annotation
TypeScript/ECMAScript Decorators
C++ Attributes
Go Tags
Attributes in Hack

かつて却下されたり放棄されたRFC。

Attributes v1
Annotations v2
Reflection Annotations using the Doc-Comment
Simple Annotations
Annotations in DocBlock RFC
Class Metadata RFC

感想

ほぼ全員賛成というのがちょっと信じがたいんだけど。
個人的にはどちらかというと賛成ですが、正直もっと意見が割れてもよさそうな提案ですよね。

ということで、PHP8からはアトリビュートが使えるようになります。
例を見るに、AttributeInterfeceをimplementsしたりとかも不要で、普通にクラスを書いたらいきなりアトリビュート名として使えるんですかねこれ。
ちょっとユーザレベルでの使い方がいまいちよくわかりませんでした。

というか正直、全体的に意味のよくわからないところが多々ありました。
私は普段ソースまで追ってないので、いきなりAST構文木とかzend_class_entryとか言われても知らんがな!ってかんじですよ。
きっと誰かが補足してくれるはず。

あと、それでは具体的にデフォルトでどんなアトリビュートが用意されてるの、ってのもRFCには書かれていません。
複雑なアトリビュートを定義するにはどうすればいいの、というのもRFCではちょっとよくわかりません。
このあたりは今後ドキュメントの追加をおねがいしたいところですね。
まあPHPのマニュアルは親切すぎて困るくらい丁寧なので、そのうち充実してくるとは思いますが。

【PHP8.0】StartsWith/EndsWithがPHP本体に実装される

先日PHP8でstr_containsが導入されることが決まったばかりですが、さらにもっと直接的な『〇〇で始まる』『〇〇で終わる』関数までも導入されることになりました。

Add str_starts_with() and str_ends_with() functionsというRFCが投票中です。
2020/05/04時点では賛成50反対4で、ほぼ導入確定です。

PHP RFC: Add str_starts_with() and str_ends_with() functions

Introduction

str_starts_withは、文字列が指定の文字列で始まるか否かをチェックし、結果をbool値で返します。
str_ends_withは、文字列が指定の文字列で終わるか否かをチェックし、結果をbool値で返します。

これらの機能は既存の文字列関数、たとえばsubstrstrpos/strrpos、そしてstrncmpsubstr_compare、あまつさえstrlenなどを駆使して実装されてきました。
これらユーザランドの実装には、様々な問題点があります。

str_starts_withとstr_ends_withの需要は高く、SymfonyLaravelYiiFuelPHP、そしてPhalconと、あらゆるフレームワークによってサポートされています。

文字列の始めと終わりをチェックすることは非常に一般的なタスクであり、簡単に行えるべきです。
多くのフレームワークがこのタスクを実装しているということは、このタスクを実行することが簡単ではないことを意味しています。
JavaScript/Java/Haskell/Matlabといった多くの高水準言語が標準でこの機能を実装している理由でもあります。
文字列の開始と終了をチェックすることは、これだけのためにフレームワークを導入したり、ユーザランドで最適ではない(どころかバグが入るかもしれない)実装を行ったりする必要のあるべき作業ではありません。

Downsides of Common Userland Approaches

この機能のアドホックな実装は、専用関数に比べると直感的ではありません。
PHPのニュービーや、他言語と同時開発する開発者にとっては特にそうです。
また、特に===を含む場合、実装を簡単に間違えます。
さらに多くのユーザランド実装はパフォーマンス上の問題があります。

注意:以下の実装には、E_WARNINGを防ぐために$needle === "" ||strlen($needle) <= strlen($haystack) &&のようなガードを入れましょう。

str_starts_with

substr($haystack,0,strlen($needle))===$needle

$haystackの無駄なコピーが発生するため、メモリ効率が良くありません。

strpos($haystack,$needle)===0

$needleが見つからなかった場合に$haystackを最後まで調べてしまうため、CPU効率が悪くなります。

strncmp($haystack,$needle,strlen($needle))===0// genericstrncmp($subject,"prefix",6)===0// ad hoc

これは効率的ですが、$needleの文字数を別に渡す必要があり冗長です。

str_ends_with

substr($haystack,-strlen($needle))===$needle

str_starts_with同様、メモリ効率がよくありません。

strpos(strrev($haystack),strrev($needle))===0

str_starts_with同様CPU効率が悪いだけでなく、文字列反転処理まで入るので、さらに非効率です。

strrpos($haystack,$needle)===strlen($haystack)-strlen($needle)

冗長であり、CPURL効率が悪くなることがあります。

substr_compare($haystack,$needle,-strlen($needle))===0// genericsubstr_compare($subject,"suffix",-6)===0// ad hoc

効率的ですが、冗長です。

Proposal

2つの関数、str_starts_with()str_ends_with()を導入します。

str_starts_with(string$haystack,string$needle):boolstr_ends_with(string$haystack,string$needle):bool

str_starts_with()は、$haystack$needleで始まるかどうかを調べます。
strlen($needle) > strlen($haystack)であれば即座にfalseを返し、そうでなければ両文字列を比較し、先頭一致すればtrueを、一致しなければfalseを返します。

str_ends_with()も同じですが、後方一致です。

以下に例を示します。

$str="beginningMiddleEnd";if(str_starts_with($str,"beg"))echo"printed\n";// trueif(str_starts_with($str,"Beg"))echo"not printed\n";// falseif(str_ends_with($str,"End"))echo"printed\n";// trueif(str_ends_with($str,"end"))echo"not printed\n";// false// 空文字if(str_starts_with("a",""))echo"printed\n";// trueif(str_starts_with("",""))echo"printed\n";// trueif(str_starts_with("","a"))echo"not printed\n";// falseif(str_ends_with("a",""))echo"printed\n";// trueif(str_ends_with("",""))echo"printed\n";// trueif(str_ends_with("","a"))echo"not printed\n";// false

空文字に関しては、受理済のstr_containsのRFCの挙動に従います。
これはJavaやPythonなどと共通の動作です。

Backward Incompatible Changes

ユーザランドに同名の関数がある場合は競合します。

Proposed PHP Version(s)

PHP8

RFC Impact

・SAPI:全てのPHP環境に関数が追加されます
・エクステンション:無し
:Opcache:無し
・New Constants:無し
・php.ini Defaults:無し

Votes

投票は2020/05/04まで。
投票者の2/3+1の賛成で受理されます。

Patches and Tests

https://github.com/php/php-src/pull/5300

References

他言語の類似機能
・JavaScript: String#startsWith() / String#endsWith()
・Python: str#startswith() / str#endswith()
・Java: String#startsWith() / String#endsWith()
・Ruby: String#start_with?() / String#end_with?()
・Go: strings.HasPrefix() / strings.HasSuffix()
・Haskell: Data.String.Utils.startswith / Data.String.Utils.endswith
・MATLAB: startsWith()) / endsWith()

bugs.php.net
bug #50434 / bug #60630 / bug #67035 / bug #74449

過去のRFC
PHP RFC: rfc:add_str_begin_and_end_functions

Rejected Features

大文字小文字を区別しない版とマルチバイト版は、以前のRFCには含まれていましたが、このRFCでは廃止されました。
理由はstr_containsを参照してください。

感想

PHPの文字列関数ってやたら大量に用意されてるわりに意外と基本的なところが抜けていたのですが、PHP8でstr_continsとこの関数が追加されたことによって、テキスト処理に必要なものは出揃うことになったのではないでしょうか。

他に必要なのって何かありますかね。デフォルト関数の命名規則とか?

【PHP8.0】なんでもあり型が書けるようになる

ジェネリクスではない…ジェネリクスではないのだよ………

ざっくり言うとvar_dump()の型引数です。
var_dumpにはプリミティブ値にオブジェクトにリソース型にと、どんな値でも渡すことができるのですが、PHP7.4時点の型システムではvar_dumpの引数の型を表すことができません。
PHP8.0で導入予定のunion型を使うとarray|bool|callable|int|float|null|object|resource|stringとなるのですが、実はresource型はPHP8.0でもまだ使えないので、mixed型を完全に再現することはできません。

ということでMixed Type v2のRFCが提出されました。
投票は2020/05/21まで、受理には2/3+1の賛成が必要です。
が2020/05/11時点では賛成35反対6で、おそらく受理されます。

Introduction

PHP7のスカラー型、7.1のnull許容型、7.2のobject型、そして最新8.0のUNION型とPHPの型システムは進化し続けており、PHPの開発者はほとんどの関数において引数と返り値、そしてプロパティについて明示的に型情報を宣言することができるようになりました。

しかし、PHPは常に型をサポートしてきたわけではありません。
そしてこれは、型情報が欠落している際にその意味が曖昧になってしまうという問題に繋がります。

・特定の型に決まっているが、プログラマが宣言を忘れてしまった
・特定の型に決まっているが、古いバージョンのPHPと互換を保つためにあえて省略している
・現在のPHPの型システムでは表現できない型である

明示的なmixed型を用意することで、引数や返り値、プロパティに型を追加して、型情報を忘れていたわけではなく、正確に指定できなかったりあえて広げているのだという主張をを示すことができます。

現在のところmixed型はPHPDocの中でのみ使用することができますが、これは適切ではありません。
PHPDocでmixed型が使用されている顕著な例としては、PHP標準ライブラリ関数の返り値などがあります。
ネイティブにmixed型があれば、これらをより正確に表現することができるでしょう。

またmixed型はPHPマニュアルにおいても広く使用されています。

var_dump(mixed$expression[,mixed$...]):void

Proposal

PHPの型システムにmixed型を追加します。
これはarray|bool|callable|int|float|null|object|resource|stringと等価です。
これはPHPの継承時の型検査の実装に適合する正しい動作です。

LSP, Covariance and Contravariance

このProposalは、リスコフの置換原則に準拠しています。

PHP7.4以降、PHPは共変戻り値と反変パラメータに対応しています。

PHPではLSP原則に従うように、パラメータの拡大を許容しています。
サブクラスにおいて、親クラスより広い、特殊でない型を使用することができます。

PHPではLSP原則に従うように、返り値の縮小を許容しています。
サブクラスにおいて、親クラスより狭い、特殊な型を使用することができます。

Parameter types are contravariant

引数は、特定の型からmixed型に拡大することができます。

// 正しい例classA{publicfunctionfoo(int$value){}}classBextendsA{// intからmixedに拡大は許可publicfunctionfoo(mixed$value){}}

引数の縮小は、LSP原則に違反するため許可されません。

// 不正な例classA{publicfunctionfoo(mixed$value){}}classBextendsA{// mixedからintに縮小は不可// Fatal errorが出るpublicfunctionfoo(int$value){}}### Return types are covariant返り値は`mixed`型から特定の型に縮小することができます```php// 正しい例classA{publicfunctionbar():mixed{}}classBextendsA{// mixedからintに縮小は許可publicfunctionbar():int{}}

返り値の拡大は、LSP原則に違反するため許可されません。

// 不正な例classC{publicfunctionbar():int{}}classDextendsC{// intからmixedに拡大は不可// Fatal errorが出るpublicfunctionbar():mixed{}}

Property types are invariant

プロパティ型指定のRFCに従い、プロパティの型は不変です。

// 不正な例classA{publicmixed$foo;publicint$bar;public$baz;}classBextendsA{// プロパティ型は縮小不可// Fatal errorが出るpublicint$foo;}classCextendsA{// プロパティ型は拡大不可// Fatal errorが出るpublicmixed$bar;}classDextendsA{// 未指定にmixed型を追加するのも駄目// Fatal errorが出るpublicmixed$baz;}classEextendsA{// 型指定の削除も駄目// Fatal errorが出るpublic$foo;}

Void return type

void型の返り値については、LSPに適合していたとしても拡張は許可されません。

classA{publicfunctionbar():void{}}classBextendsA{// Fatal error: Declaration of B::bar(): int must be compatible with A::bar(): voidpublicfunctionbar():int{}}

このRFCは、既存の振る舞いに従います。
すなわち、void型をmixed型に広げることはできません。

Signature checking of function when no parameter type present

引数に型が存在しない場合の型チェックは、mixed型が指定されたかのように動作します。

classA{// 引数の型がないのでmixedとみなすpublicfunctionfoo($value){}}classBextendsA{// mixed型を追加したが、親クラスと動きは同じpublicfunctionfoo(mixed$value){}}classCextendsB{// mixed型を削除したが、親クラスと動きは同じpublicfunctionfoo($value){}}classDextendsB{publicfunctionfoo(mixed$value=null){}}

現在のところ、これはクラスの継承に限った動作です。

PHPで型を定義できるようになれば、他のところでも動くようになるかもしれません。

Signature checking of function when no return type present

返り値に型が指定されていない場合の型チェックは、mixed|void型が指定されたかのように動作します。

サブクラスでオーバーロードする際は、返り値を未指定にするか、void型にするか、mixed型およびそのサブタイプの何れかを指定しなければなりません。
そして、一度変更したあとの型を未指定に戻すことはできません。

classA{// 返り値に型がないのでmixed|voidとみなすpublicfunctionfoo(){}}classBextendsA{// mixedを指定した。voidは禁止になるpublicfunctionfoo():mixed{}}classCextendsB{// mixed|voidはmixedより広いのでNG// Fatal errorが出るpublicfunctionfoo(){}}classDextendsB{// voidはmixedのサブタイプではないのでNG// Fatal errorが出るpublicfunctionfoo():void{}}

The mixed|void union type

このRFCは、mixed|voidのUNION型は必要ないので許可しないという立場です。
今後ユースケースが見つかれば許可される可能性はあります。

Nullability

mixed型にはnullが含まれます。
従ってmixed型のnull許容型は情報の重複となります。

このRFCは、mixed型のnull許容型を許容しないという立場です。
今後ユースケースが見つかれば許可される可能性はありますが、その際はどの冗長な型指定を許可して、どの冗長な型指定は許可しない、という議論が必要になるでしょう。

// NG、既にnull許容なのでfunctionfoo(?mixed$arg){}// NG、既にnull許容なのでfunctionbar():?mixed{}

Explicit returns

返り値にmixed型を使用する場合、明示的にreturnを記述する必要があります。
さもなければTypeErrorが発生します。

functionfoo():mixed{}foo();// Uncaught TypeError: Return value of foo() must be of the type mixed, none returned

既存のnull許容型と同じ動作です。

functionbar():?int{}bar();// Uncaught TypeError: Return value of bar() must be of the type int or null, none returned

Resource 'type'

PHPではresource型の値を変数に割り当てることができますが、ユーザランドでは引数、返り値、プロパティの型としてresource型を使用することができません。
このRFCの立場としては、resource型はresource型チェックをパスすべきである、というものです。

Mixed vs any

PHPではマニュアルやPHPStanなどの静的解析ツールで広くmixed型が使われているため、mixedになりました。
またmixedはPHP7以降弱い予約語とされていますが、anyは予約語に含まれません。

RFC Impact

Proposed PHP Version(s)

PHP8.0

Backward Incompatible Changes

クラス名としてのmixedが禁止されますが、PHP7.0以降mixedは弱い予約語です。

To SAPIs

特になし。

To Existing Extensions

特になし。

To Opcache

特になし。

Vote

2020/05/07に投票開始、2020/05/21に投票終了。
受理には2/3+1の賛成が必要です。

Patches and Tests

GitHub Pull request #5313

References

PHP RFC: Reserve Even More Types in PHP 7
phpDocumentor type reference

感想

これ、UNION型のRFCの将来の展望にある型宣言そのものですよね。
ついでだから型宣言自体もできるようにしてしまえばいいのでは。

ということでPHP8からmixed型が使えるようになります。

var_dumpのような、仕様としてあらゆる値を受け取る必要のある関数のためにこれが必要なことは確かでしょう。

しかし、ユーザランドで何も考えず適当にこれを使って大惨事、という未来が目に見えますね。
まあでも、そんな人はそもそも型引数を書かないだろうから問題ないかな?

これがなくては生きていけないVS Codeエクステンション10選

VisualStudioCodeは大人気なだけあって、有能なエクステンションが次から次へと出てきますね。
色々とっかえひっかえ試して遊んでいる人も多いのではないでしょうか。
なんかMinecraftのMOD環境構築と同じ空気を感じますね。
(環境ができたところで飽きて遊ばなくなる)

まあ一番手っ取り早いのは、既に評価の固まっている手堅いエクステンションを導入することです。
ということで以下はSahil Bondre( Twitter / GitHub / LinkedIn / Instagram / 個人HP )による記事、💡 10 VS Code extensions I can't code withoutの日本語訳です。

💡 10 VS Code extensions I can't code without

VSCodeは私のお気に入りのエディタです。
存在するコードエディタの中でも最も拡張性の高く、人気のあるエディタです。
そして驚いたことに、これはMicrosoftが作っています。
個人的には、他のエディタはVSCodeの機能の半分にも達しているものはないと感じています。
VSCodeをこれほど確固たる地位に押し上げた理由が、そのエクステンションのシステムです。
これにより、おおよそあらゆるユースケースに対応した拡張を書くことができます。
以下で私的エクステンション、トップ10を紹介します。

1. Beautify

ext install HookyQR.beautify

HTML、JS、CSS、JSON、そしてSASSを自分好みのスタイルでフォーマットすることができます。
内部のjs-beautifyを拡張して、自分だけのスタイルにカスタマイズすることもできます。

2. Better Comments

02.png

ext install aaron-bond.better-comments

コメントにセマンティクスを追加することができます。
コメントをアラート、クエリ、TODO、ハイライトのように分類してくれます。

021.png

3. Bookmarks

03.png

何百行ものコードの中で迷子になってしまったときに、このエクステンションが救世主となるでしょう。
コードの任意の行をブックマークすることができます。
ブックマークの一覧から選択するだけで、該当の場所に戻ってくることができます。

031.png

4. Bracket Pair Colorizer 2

04.png

ext install CoenraadS.bracket-pair-colorizer-2

この機能は、その名のとおりです。
括弧を多重に使用した際にペアごとに色分けします。
コードの奥深くに幾つものスコープがあるときに役立つでしょう。

041.png

5. Format in Context Menus

05.png

ext install lacroixdavid1.vscode-format-context-menu

サイドバーから選択するだけで、全てのファイルをフォーマットすることができます。
大量のファイルがあり、そしてあなたの環境がFormatterやLinterをサポートしていない場合に特に有用です。

051.gif

6. Git Graph

06.png

ext install mhutchie.git-graph

リポジトリのGitグラフを表示し、グラフからGitアクションを素早く実行することができます。
高度な設定が可能で、多くの機能を備えています。
このエクステンションの素晴らしさを布教するため、私はきっとまた別の記事を書くことになるでしょう。

061.gif

7. Git Lens

07.png

ext install eamodio.gitlens

GitLensは、blameとCodeLensによってコードのオーサリングを手助けしてくれます。
Gitリポジトリをシームレスにナビゲートしてエクスクローラーしたり、有能な比較コマンドを用いて知見を得たり、その他多くのことが可能です。

071.png

8. indent-rainbow

08.png

ext install oderwat.indent-rainbow

コードのインデントを深さごとに着色してくれます。
これは深いインデントのあるPythonのような言語で特に有用です。

9. Path Intellisense

09.png

ext install christian-kohler.path-intellisense

ファイルパスを自動補完してくれるようになります。
VSCodeはデフォルトでも自動補完をサポートしてますが、それはHTML/CSS/JavaScriptファイルに限定されています。
このエクステンションであらゆる言語とファイルタイプに拡張されます。

091.gif

10. Total Lines

10.png

ext install praveencrony.total-lines

ステータスバーに現在開いているファイルの行数を表示するだけの、小さなエクステンションです。
あなたのエクステンションのコレクションに追加されるべき良いものです。

これが私のお気に入りVSCodeエクステンションです。
あなたのお気に入り拡張機能をコメントでおしえてくれませんか?
ところでBashを学びたくありませんか?
シェルスクリプトのいいクラッシュコースありますよ

🌟 いくつかチートシート作ってるよ。
🚀 Instagram | Github | Twitter | Websiteでストーキングしてくれていいのよ。
😄 素敵な一日を過ごせますように!

コメント欄

「VSC半年使ってたのにBookmarks知らんかった。いったいどれだけ時間を無駄にしたのだろう。」「Codeletsでブックマーク。こいつはいいぞ最高だぞ。」
「Bookmarksさっそくインストールして、コードを後で参考にするために使ってる。」
「Bracket pair colorizer気に入った。ありがとう。」
「未知のエクステンションばっかりだった、さっそく試してみる。」
「HTML、CSS、JS、Java、Node、Ruby、他いろいろエクステンションを入れてたらあまりに巨大になりすぎた。どうにかしなければならないのではないか。」「対象に合わせて拡張パックを作って、それらの有効/無効を切り替えるってやってる。」「それはいいことを聞いた。」「よかったら私の拡張パックを紹介するよ。」

感想

私の環境では既に半分インストール済でした。
星の数ほどエクステンションがあるとは言っても、やはり汎用的に有用なものとなるとある程度数は限られてきますね。

コメント欄ではBookmarksがやたら高評価でした。
気軽に多くの付箋を置いておいて、後からすぐにそこに戻れるので、なかなか便利です。
私のように見終わったタブをついうっかり閉じてしまう癖のある人には、より有用だと思われます。

あとGit Graphも入れてなかったので入れてみたのですが、これ便利だな。
これまでわざわざ別ウィンドウでTortoiseGitなりGit bashなりを立ち上げていたのですが、VSCode上でGitツリーを表示して、ツリー上で各種操作を簡単に行うことができます。
これは使い方を調べてみる価値がありそうです。

とまあこんなかんじでエクステンションを入れまくっていたら、今何が入っててバージョンがどうなってるとかの管理がさっぱりになります。
既になっています。
言語や開発対象ごとに有効無効を切り替えたいときもあるでしょう。
そんなときのためにエクステンションを管理するエクステンションがほしいところですね。

JavaScriptのアロー関数が好きではない

JavaScriptのアロー関数が好きではない。

constf=()=>{a++;};leta=1;f();console.log(a);// 2 ←

こんな仕様当然みんなからフルボッコのWTF案件だろうと思いきや、批判以前にそもそもこの仕様について言及している記事自体がほとんど見当たらない。
MDNにすらはっきりとは書かれていない。
仕様書まで追えば当然載ってはいるんだけど、こんなところまでいちいち読んでられませんよね。

まあスコープチェーンというJavaScriptデフォルトの仕様のせいであるわけですが、しかしわざわざ最近追加された仕組みなのだからこんなクソ仕様は断ち切ってほしかったところですね。
アロー関数の仕様を決めるときに誰も何も言わなかったのか?

【PHP8.0】例外をcatchしたいけど何もしたくない

例外をcatchしたいけど何もしたくない。

try{foo();}catch(Throwable$e){// 何もしない}

何もしないのにわざわざ変数に受け取るのって無駄じゃありませんか?

というわけでnon-capturing catchesというRFCが提出されました。

PHP RFC: non-capturing catches

Introduction

今のところ、PHPは例外を明示的に変数でキャプチャする必要があります。

try{foo();}catch(SomeException$ex){die($ex->getMessage());}

しかしながら、ときにはその変数を使わない場合もあります。

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

これを見た人は、プログラマが意図してこの変数を使わなかったのか、あるいはバグなのかがわかりません。

Proposal

例外を変数に受け取らずcatchできるようにします。

try{changeImportantData();}catch(PermissionException){// catchした変数を使わないという意図が明白echo"You don't have permission to do this";}

Prior art

7年前にあった似たようなRFCは、以下のように例外名も省略可能にするという理由で否定意見が多数でした。

try{foo();}catch{bar();}

いっぽう、このRFCは肯定的な反応が多く、再検討の余地があります。

Backward Incompatible Changes

後方互換性を壊す変更はありません。

Proposed PHP Version(s)

PHP8.0

RFC Impact

特になし。

Vote

投票は2020/05/24まで、投票者の2/3の賛成で受理されます。
このRFCは賛成48反対1の圧倒的多数で受理されました。

Patches and Tests

https://github.com/php/php-src/pull/5345

References

メーリングリスト

感想

このRFCが意図しているところは決して例外の握り潰しなどではなく、例外処理のロジックに例外の中身を使わないときに省略できるというものです。
変数に受け取る処理がなくなるので速度も速まることでしょう。

しかしですな、こんな機能があったら絶対にこんなコードを書く奴が出てくるわけですよ。

PHP8
try{foo();}catch(Exception){}

いやまあ、こんなの書いてしまう人は今でもやってると思いますけどね。

PHP7
try{foo();}catch(Exception$e){}

なら別にあってもいいか。

劇的に便利になるというわけではないですが、ちょっと気が利く書き方ができるようになりますね。

最後にもう一度言いますが、冒頭のコードは悪い例なので決して真似してはいけません。

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

これまで何度も塩漬けにされたり却下されたりしていたオブジェクト初期化子ですが、ついに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ニュースの過去記事が見えない

そもそも今日初めて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が間違っていた(しかも半端に動くせいで気付かなかった)というだけの話だった。

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

今回のコードの汚さには正直ちょっと自信あるよ。
下手すりゃ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の統計レポート

以下は、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式が使えるようになる

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の威力を体感する(した)

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選

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万部を超えるようなベストセラーばかりです。
読めば自らのためになることは確定的に明らかです。

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

Browsing Latest Articles All 67 Live