PHP の実行原理
php は、非常にシンプルなアプリケーションと非常に高い開発効率を備えた言語であり、その弱い型指定変数により、プログラマは変数の定義や型変換などにかかる時間と労力を大幅に節約できます。 Web開発に適した動的言語です。
マルチプロセス モデル: これにより、プロセスが相互に影響を受けなくなり、プロセスのリソース利用がより速く、より便利になります
弱い型付け言語: C、C++、Java などの言語とは異なり、PHP の変数の型は実行時に暗黙的または明示的に決定されるため、使いやすくなっています。開発は非常に柔軟であり、プログラマは変数の型に注意を払う必要がありません。Zend エンジン + コンポーネント (ext) モードにより、Web サーバーと PHP が分離されます。柔軟性があり、標準化されていません。これには長所と短所があります。 。 。
2. PHP の 4 層システム
: Zend エンジンの周りでは、拡張機能はコンポーネントを通じてさまざまな基本サービスを提供し、一般的に使用される組み込み関数配列、標準ライブラリなどはすべて拡張機能を通じて実装されます。 Tiebaで使用されるPHP中間層やリッチテキスト解析など、機能拡張の目的を達成するための拡張の代表的な応用例です。
Sapi: Sapi の正式名はサーバー アプリケーション プログラミング インターフェイスです。これは、PHP が一連のフック関数を通じて周辺データと対話できるようにする、非常にエレガントで成功した PHP の設計です。 . sapi は、PHP 自体を上位層のアプリケーションから切り離し、分離することに成功しました。PHP は、異なるアプリケーションとの互換性を考慮する必要がなくなり、アプリケーション自体も独自の特性に応じて異なる処理方法を実装できます。
上位層アプリケーション: これは、プログラマーによって作成されたアプリケーションであり、Web サーバーを介した Web アプリケーションの実装、コマンド ラインでのスクリプトとしての実行など、さまざまな SAPI メソッドを通じて取得されます。
3. Sapiapache2handler: apache を使用します。これは現在最も広く使用されている方法であり、Web サーバーと php の間のもう 1 つの対話方法であり、fastcgi プロトコルです
。コマンドデバッグアプリケーションモード
PHP 実行の中核は、命令を 1 つずつ翻訳することです。つまり、
オペコードよく使用されるいくつかの関数:
ZEND_CONCAT_SPEC_CV_CV_HAN DLER : 文字列連結 a.b
ZEND_ADD_SPEC_CV_CONST_HANDLER: 加算 a+2
ZEND_IS_EQUAL_SPEC_CV_CONST: 同等性の判断 a==1Zend ハッシュ テーブルは、典型的なハッシュ テーブルのハッシュ構造を実装すると同時に、双方向のリンク リストを付加することにより、順方向、逆方向、および配列の走査の機能を提供します。構造は図に示すとおりです。
ご覧のとおり、ハッシュ テーブルには、キー -> 値の形式のハッシュ構造と二重リンク リスト モードの両方があり、高速検索と線形トラバーサルをサポートするのに非常に便利です。
ハッシュ構造: Zend のハッシュ構造は典型的なハッシュ テーブル モデルであり、リンク リストを通じて競合を解決します。 Zend のハッシュ テーブルは自己成長するデータ構造であり、ハッシュ テーブルがいっぱいになると動的に 2 倍に拡張され、要素の位置が変更されることに注意してください。初期サイズは8です。さらに、キー->値の高速検索を実行する場合、zend 自体も空間を時間に交換することでプロセスを高速化するいくつかの最適化を行っています。たとえば、変数 nKeyLength は各要素で使用され、キーの長さを識別して迅速に決定します。
ダブルリンクリスト: Zend ハッシュテーブルは、リンクリスト構造を介した要素の線形走査を実装します。理論的には、走査には一方向リンク リストを使用するだけで十分です。二重リンク リストを使用する主な目的は、迅速に削除して走査を回避することです。 Zend ハッシュ テーブルは複合構造であり、配列として使用すると、一般的な連想配列をサポートし、連続したインデックス番号として使用したり、2 つを混合したりすることもできます。
PHP 連想配列: 連想配列は典型的な hash_table アプリケーションです。クエリ プロセスは次の手順を実行します (コードからわかるように、これは一般的なハッシュ クエリ プロセスであり、検索を高速化するためにいくつかの素早い判断が追加されています):
01 getKeyHashValue h; 02 index = n & nTableMask; 03 Bucket *p = arBucket[index]; 04 while (p) { 05 if ((p->h == h) && (p->nKeyLength == nKeyLength)) { 06 RETURN p->data; 07 } 08 p=p->next; 09 } 10 RETURN FALTURE;
PHP インデックス配列: インデックス配列は次のとおりです。次のターゲット アクセスを通じて、共通の配列を取得します。たとえば、arr[0]、Zend HashTable は内部で正規化されており、インデックス タイプのキーにもハッシュ値と nKeyLength (0) が割り当てられています。内部メンバー変数 nNextFreeElement は現在割り当てられている最大 ID であり、プッシュするたびに自動的に 1 ずつ増加します。この正規化プロセスにより、PHP は連想データと非連想データの混合を実現できます。 プッシュ操作の特殊性により、PHP 配列内のインデックス キーの順序は添え字のサイズではなく、プッシュの順序によって決まります。 例えば、arr[1] = 2; arr[2] = 3; 場合、Zend HashTable はそれをインデックスキーとして扱います
5.2 PHP 変数の実装原理
PHP は弱い型付けですlanguage では、変数の型を厳密に区別しません。 PHP 変数は、単純型 (int、sting、bool)、コレクション型 (array、resource、object)、および定数 (const) に分類できます。すべての変数の下部は同じ構造になっています zval
zval は zend A にあります。 PHP 変数をマークおよび実装するために使用される非常に重要なデータ構造は次のとおりです:
struct _zval_struct { zvalue_value value; /* value */ zend_uint refcount__gc; /* variable ref count */ zend_uchar type; /* active type */ zend_uchar is_ref__gc; /* if it is a ref variable */ }; typedef struct _zval_struct zval;
その中で、
zval_value value は変数の実際の値、具体的には zvalue_value 共用体です:
typedef union _zvalue_value { long lval; /* long value */ double dval; /* double value */ struct { /* string */ char *val; int len; } str; HashTable *ht; /* hash table value,used for array */ zend_object_value obj; /* object */ } zvalue_value;
zend_uint refcount__gc は、zval を指す変数 (またはシンボル、シンボル) の数を保存するために使用されるカウンターです。変数が生成されると、その refcount=1 になります。$a = $b などの一般的な代入操作では zval の refcount が 1 ずつ増加し、それに応じて unset 操作によって 1 ずつ減少します。 PHP5.3 より前では、GC の実装に参照カウント メカニズムが使用されていました。zval の refcount が 0 未満の場合、Zend エンジンは zval を指す変数が存在しないと判断し、その結果、zval が占有しているメモリ空間を解放していました。ズヴァル。しかし、物事はそれほど単純ではない場合もあります。 zval を指す変数が設定されていない場合でも、単純な参照カウント メカニズムでは循環参照される zval を GC できず、その結果メモリ リーク (メモリ リーク) が発生することが後でわかります。
zend_uchar typeこのフィールドは、変数の実際の型を示すために使用されます。 PHP の変数には、4 つのスカラー型 (bool、int、float、string)、2 つの複合型 (配列、オブジェクト)、および 2 つの特殊型 (リソースと NULL) が含まれます。 zend 内部では、これらの型は次のマクロ (コードの場所 phpsrc/Zend/zend.h) に対応します。
#define IS_NULL 0 #define IS_LONG 1 #define IS_DOUBLE 2 #define IS_BOOL 3 #define IS_ARRAY 4 #define IS_OBJECT 5 #define IS_STRING 6 #define IS_RESOURCE 7 #define IS_CONSTANT 8 #define IS_CONSTANT_ARRAY 9 #define IS_CALLABLE 10
is_ref__gc このフィールドは、変数が参照変数であるかどうかをマークするために使用されます。通常の変数の場合は値が 0、参照変数の場合は値が 1 になります。この変数は、zval
の共有、分離などに影響します。整数と浮動小数点数は、PHP の基本型の 1 つであり、単純な変数でもあります。整数と浮動小数点数の場合、対応する値は zvalue に直接格納されます。タイプはそれぞれlongとdoubleです。
整数型の場合、C などの厳密に型指定された言語とは異なり、PHP では int、unsigned int、long、long long などの型が 1 つだけであることがわかります。単なる整数型です。このことから、PHP では整数の値の範囲はコンパイラのビット数によって決まり、固定されていないことがわかります。 PHP で整数が範囲外になったらどうなりますか? PHP は整数を浮動小数点型に自動的に変換します
浮動小数点数の場合は、整数と同様に、float と double を区別せず、double の 1 つの型のみを持ちます
整数と同様、文字変数は、PHP の基本型と単純な変数でもあります。 zvalue 構造から、PHP の文字列は、C++ の文字列と同様に、実際のデータへのポインターと長さ構造で構成されていることがわかります。 c とは異なり、長さは実際の変数で表されるため、その文字列はバイナリ データ (0 を含む) にすることができます。同時に、PHP では、文字列の長さ strlen を求めるのは O(1) 操作です
一般的な文字列の比較スプライシング方法と速度の比較:
次の 4 つの変数があるとします: strA='123'; intA=123'; intB=456; 次に、次の文字列スプライシング方法と説明を比較します。 :
1 res = strA.strB および res = "strAstrB"
この場合、zend はメモリの一部を再割り当てし、それに応じて処理します。その速度は平均的です。
2 strA = strA.strB
これが最も高速です。zend はコピーの繰り返しを避けるために、現在の strA に基づいて直接再割り当てします
3 res = intA.intB
これは暗黙的な書式設定の変換が必要なため遅くなります。また、次の点にも注意する必要があります。実際にプログラムを書くときは避けてください
4 strA = sprintf ("%s%s", strA, strB);
sprintf は PHP の言語構造ではないため、これは最も遅い方法になります。フォーマットを識別して処理するメカニズム自体は malloc です。ただし、sprintf メソッドは最も可読性が高く、実際には特定の状況に応じて柔軟に選択できます。
foreach操作を実装するにはどうすればよいですか? 配列の Foreach は、ハッシュテーブル内の二重リンク リストを走査することによって完了します。インデックス配列の場合、foreach を介した走査は for よりもはるかに効率的であり、キー→値を検索する必要がなくなります。 count オペレーションは、HashTable->NumOfElements, O(1) オペレーションを直接呼び出します。 「123」のような文字列の場合、zend はそれを整数形式に変換します。 arr[‘123’] と arr[123]
5.3 PHP 変数管理 - 参照カウントとコピーオンライト参照カウントは、メモリのリサイクル、文字列操作などで広く使用されています。 Zval の参照カウントは、メンバー変数 is_ref および ref_count によって実装されます。参照カウントを通じて、複数の変数が同じデータを共有できます。頻繁なコピーによる大量の消費を避けてください。代入操作中、zend は変数を同じ zval および ref_count++に指し、設定解除操作中は、対応する ref_count-1 を指します。破棄操作は、ref_count が 0 に減少した場合にのみ実行されます。 それが参照割り当ての場合、zend は is_ref を 1 に変更します。 PHP 変数は参照カウントを通じて変数共有データを実現しますが、変数の 1 つの値を変更した場合はどうなるでしょうか?変数を書き込もうとするときに、その変数が指す zval が複数の変数で共有されていることがわかると、Zend は ref_count が 1 の zval をコピーし、元の zval の refcount をデクリメントします。このプロセスは「zval 分離」と呼ばれます。 」。 zend は書き込み操作が発生したときにのみコピー操作を実行することがわかります。そのため、
copy-on-write (コピーオンライト) とも呼ばれます。参照変数の場合、要件は非参照型とは逆です。 ,
参照が割り当てられた変数はバンドルする必要があります。1 つの変数を変更すると、バンドルされたすべての変数が変更されます。5.4 PHP でのローカル変数とグローバル変数の実装:
変数値の取得: PHP のシンボル テーブルは hash_table を通じて実装され、取得時に対応する zval がテーブルから検索され、識別子に従って返されます。 関数でグローバル変数を使用する: 関数では、明示的に global を宣言することでグローバル変数を使用できます。
active_symbol_tableのsymbol_tableに同じ名前の変数への参照を作成します(参照変数の値を更新する必要がある場合、symbol_tableに同じ名前の変数が存在しない場合は、全員が一緒に更新します)。最初に作成されます。
参考:
http://www.php.cn/
http://www.php.cn/
[http://www.php.cn/
PHP の実行原理
php は、非常にシンプルなアプリケーションと非常に高い開発効率を備えた言語であり、その弱い型指定変数により、プログラマは変数の定義や型変換などにかかる時間と労力を大幅に節約できます。 Web開発に適した動的言語です。
マルチプロセス モデル: これにより、プロセスが相互に影響を受けなくなり、プロセスのリソース利用がより速く、より便利になります
弱い型付け言語: C、C++、Java などの言語とは異なり、PHP の変数の型は実行時に暗黙的または明示的に決定されるため、使いやすくなっています。開発は非常に柔軟であり、プログラマは変数の型に注意を払う必要がありません。Zend エンジン + コンポーネント (ext) モードにより、Web サーバーと PHP が分離されます。柔軟性があり、標準化されていません。これには長所と短所があります。 。 。
2. PHP の 4 層システム
: Zend エンジンの周りでは、拡張機能はコンポーネントを通じてさまざまな基本サービスを提供し、一般的に使用される組み込み関数配列、標準ライブラリなどはすべて拡張機能を通じて実装されます。 Tiebaで使用されるPHP中間層やリッチテキスト解析など、機能拡張の目的を達成するための拡張の代表的な応用例です。
Sapi: Sapi の正式名はサーバー アプリケーション プログラミング インターフェイスです。これは、PHP が一連のフック関数を通じて周辺データと対話できるようにする、非常にエレガントで成功した PHP の設計です。 . sapi は、PHP 自体を上位層のアプリケーションから切り離し、分離することに成功しました。PHP は、異なるアプリケーションとの互換性を考慮する必要がなくなり、アプリケーション自体も独自の特性に応じて異なる処理方法を実装できます。
上位層アプリケーション: これは、プログラマーによって作成されたアプリケーションであり、Web サーバーを介した Web アプリケーションの実装、コマンド ラインでのスクリプトとしての実行など、さまざまな SAPI メソッドを通じて取得されます。
3. Sapiapache2handler: apache を使用します。これは現在最も広く使用されている方法であり、Web サーバーと php の間のもう 1 つの対話方法であり、fastcgi プロトコルです
。コマンドデバッグアプリケーションモード
PHP 実行の中核は、命令を 1 つずつ翻訳することです。つまり、
オペコードよく使用されるいくつかの関数:
ZEND_CONCAT_SPEC_CV_CV_HAN DLER : 文字列連結 a.b
ZEND_ADD_SPEC_CV_CONST_HANDLER: 加算 a+2
ZEND_IS_EQUAL_SPEC_CV_CONST: 同等性の判断 a==1Zend ハッシュ テーブルは、典型的なハッシュ テーブルのハッシュ構造を実装すると同時に、双方向のリンク リストを付加することにより、順方向、逆方向、および配列の走査の機能を提供します。構造は図に示すとおりです。
ご覧のとおり、ハッシュ テーブルには、キー -> 値の形式のハッシュ構造と二重リンク リスト モードの両方があり、高速検索と線形トラバーサルをサポートするのに非常に便利です。
ハッシュ構造: Zend のハッシュ構造は典型的なハッシュ テーブル モデルであり、リンク リストを通じて競合を解決します。 Zend のハッシュ テーブルは自己成長するデータ構造であり、ハッシュ テーブルがいっぱいになると動的に 2 倍に拡張され、要素の位置が変更されることに注意してください。初期サイズは8です。さらに、キー->値の高速検索を実行する場合、zend 自体も空間を時間に交換することでプロセスを高速化するいくつかの最適化を行っています。たとえば、変数 nKeyLength は各要素で使用され、キーの長さを識別して迅速に決定します。
ダブルリンクリスト: Zend ハッシュテーブルは、リンクリスト構造を介した要素の線形走査を実装します。理論的には、走査には一方向リンク リストを使用するだけで十分です。二重リンク リストを使用する主な目的は、迅速に削除して走査を回避することです。 Zend ハッシュ テーブルは複合構造であり、配列として使用すると、一般的な連想配列をサポートし、連続したインデックス番号として使用したり、2 つを混合したりすることもできます。
PHP 連想配列: 連想配列は典型的な hash_table アプリケーションです。クエリ プロセスは次の手順を実行します (コードからわかるように、これは一般的なハッシュ クエリ プロセスであり、検索を高速化するためにいくつかの素早い判断が追加されています):
01 getKeyHashValue h; 02 index = n & nTableMask; 03 Bucket *p = arBucket[index]; 04 while (p) { 05 if ((p->h == h) && (p->nKeyLength == nKeyLength)) { 06 RETURN p->data; 07 } 08 p=p->next; 09 } 10 RETURN FALTURE;
PHP インデックス配列: インデックス配列は次のとおりです。次のターゲット アクセスを通じて、共通の配列を取得します。たとえば、arr[0]、Zend HashTable は内部で正規化されており、インデックス タイプのキーにもハッシュ値と nKeyLength (0) が割り当てられています。内部メンバー変数 nNextFreeElement は現在割り当てられている最大 ID であり、プッシュするたびに自動的に 1 ずつ増加します。この正規化プロセスにより、PHP は連想データと非連想データの混合を実現できます。 プッシュ操作の特殊性により、PHP 配列内のインデックス キーの順序は添え字のサイズではなく、プッシュの順序によって決まります。 例えば、arr[1] = 2; arr[2] = 3; 場合、Zend HashTable はそれをインデックスキーとして扱います
5.2 PHP 変数の実装原理
PHP は弱い型付けですlanguage では、変数の型を厳密に区別しません。 PHP 変数は、単純型 (int、sting、bool)、コレクション型 (array、resource、object)、および定数 (const) に分類できます。すべての変数の下部は同じ構造になっています zval
zval は zend A にあります。 PHP 変数をマークおよび実装するために使用される非常に重要なデータ構造は次のとおりです:
struct _zval_struct { zvalue_value value; /* value */ zend_uint refcount__gc; /* variable ref count */ zend_uchar type; /* active type */ zend_uchar is_ref__gc; /* if it is a ref variable */ }; typedef struct _zval_struct zval;
その中で、
zval_value value は変数の実際の値、具体的には zvalue_value 共用体です:
typedef union _zvalue_value { long lval; /* long value */ double dval; /* double value */ struct { /* string */ char *val; int len; } str; HashTable *ht; /* hash table value,used for array */ zend_object_value obj; /* object */ } zvalue_value;
zend_uint refcount__gc は、zval を指す変数 (またはシンボル、シンボル) の数を保存するために使用されるカウンターです。変数が生成されると、その refcount=1 になります。$a = $b などの一般的な代入操作では zval の refcount が 1 ずつ増加し、それに応じて unset 操作によって 1 ずつ減少します。 PHP5.3 より前では、GC の実装に参照カウント メカニズムが使用されていました。zval の refcount が 0 未満の場合、Zend エンジンは zval を指す変数が存在しないと判断し、その結果、zval が占有しているメモリ空間を解放していました。ズヴァル。しかし、物事はそれほど単純ではない場合もあります。 zval を指す変数が設定されていない場合でも、単純な参照カウント メカニズムでは循環参照される zval を GC できず、その結果メモリ リーク (メモリ リーク) が発生することが後でわかります。
zend_uchar typeこのフィールドは、変数の実際の型を示すために使用されます。 PHP の変数には、4 つのスカラー型 (bool、int、float、string)、2 つの複合型 (配列、オブジェクト)、および 2 つの特殊型 (リソースと NULL) が含まれます。 zend 内部では、これらの型は次のマクロ (コードの場所 phpsrc/Zend/zend.h) に対応します。
#define IS_NULL 0 #define IS_LONG 1 #define IS_DOUBLE 2 #define IS_BOOL 3 #define IS_ARRAY 4 #define IS_OBJECT 5 #define IS_STRING 6 #define IS_RESOURCE 7 #define IS_CONSTANT 8 #define IS_CONSTANT_ARRAY 9 #define IS_CALLABLE 10
is_ref__gc このフィールドは、変数が参照変数であるかどうかをマークするために使用されます。通常の変数の場合は値が 0、参照変数の場合は値が 1 になります。この変数は、zval
の共有、分離などに影響します。整数と浮動小数点数は、PHP の基本型の 1 つであり、単純な変数でもあります。整数と浮動小数点数の場合、対応する値は zvalue に直接格納されます。タイプはそれぞれlongとdoubleです。
整数型の場合、C などの厳密に型指定された言語とは異なり、PHP では int、unsigned int、long、long long などの型が 1 つだけであることがわかります。単なる整数型です。このことから、PHP では整数の値の範囲はコンパイラのビット数によって決まり、固定されていないことがわかります。 PHP で整数が範囲外になったらどうなりますか? PHP は整数を浮動小数点型に自動的に変換します
浮動小数点数の場合は、整数と同様に、float と double を区別せず、double の 1 つの型のみを持ちます
整数と同様、文字変数は、PHP の基本型と単純な変数でもあります。 zvalue 構造から、PHP の文字列は、C++ の文字列と同様に、実際のデータへのポインターと長さ構造で構成されていることがわかります。 c とは異なり、長さは実際の変数で表されるため、その文字列はバイナリ データ (0 を含む) にすることができます。同時に、PHP では、文字列の長さ strlen を求めるのは O(1) 操作です
一般的な文字列の比較スプライシング方法と速度の比較:
次の 4 つの変数があるとします: strA='123'; intA=123'; intB=456; 次に、次の文字列スプライシング方法と説明を比較します。 :
1 res = strA.strB および res = "strAstrB"
この場合、zend はメモリの一部を再割り当てし、それに応じて処理します。その速度は平均的です。
2 strA = strA.strB
これが最も高速です。zend はコピーの繰り返しを避けるために、現在の strA に基づいて直接再割り当てします
3 res = intA.intB
これは暗黙的な書式設定の変換が必要なため遅くなります。また、次の点にも注意する必要があります。実際にプログラムを書くときは避けてください
4 strA = sprintf ("%s%s", strA, strB);
sprintf は PHP の言語構造ではないため、これは最も遅い方法になります。フォーマットを識別して処理するメカニズム自体は malloc です。ただし、sprintf メソッドは最も可読性が高く、実際には特定の状況に応じて柔軟に選択できます。
foreach操作を実装するにはどうすればよいですか? 配列の Foreach は、ハッシュテーブル内の二重リンク リストを走査することによって完了します。インデックス配列の場合、foreach を介した走査は for よりもはるかに効率的であり、キー→値を検索する必要がなくなります。 count オペレーションは、HashTable->NumOfElements, O(1) オペレーションを直接呼び出します。 「123」のような文字列の場合、zend はそれを整数形式に変換します。 arr[‘123’] と arr[123]
5.3 PHP 変数管理 - 参照カウントとコピーオンライト参照カウントは、メモリのリサイクル、文字列操作などで広く使用されています。 Zval の参照カウントは、メンバー変数 is_ref および ref_count によって実装されます。参照カウントを通じて、複数の変数が同じデータを共有できます。頻繁なコピーによる大量の消費を避けてください。代入操作中、zend は変数を同じ zval および ref_count++に指し、設定解除操作中は、対応する ref_count-1 を指します。破棄操作は、ref_count が 0 に減少した場合にのみ実行されます。 それが参照割り当ての場合、zend は is_ref を 1 に変更します。 PHP 変数は参照カウントを通じて変数共有データを実現しますが、変数の 1 つの値を変更した場合はどうなるでしょうか?変数を書き込もうとするときに、その変数が指す zval が複数の変数で共有されていることがわかると、Zend は ref_count が 1 の zval をコピーし、元の zval の refcount をデクリメントします。このプロセスは「zval 分離」と呼ばれます。 」。 zend は書き込み操作が発生したときにのみコピー操作を実行することがわかります。そのため、
copy-on-write (コピーオンライト) とも呼ばれます。参照変数の場合、要件は非参照型とは逆です。 ,
参照が割り当てられた変数はバンドルする必要があります。1 つの変数を変更すると、バンドルされたすべての変数が変更されます。5.4 PHP でのローカル変数とグローバル変数の実装:
変数値の取得: PHP のシンボル テーブルは hash_table を通じて実装され、取得時に対応する zval がテーブルから検索され、識別子に従って返されます。 関数でグローバル変数を使用する: 関数では、明示的に global を宣言することでグローバル変数を使用できます。
active_symbol_tableのsymbol_tableに同じ名前の変数への参照を作成します(参照変数の値を更新する必要がある場合、symbol_tableに同じ名前の変数が存在しない場合は、全員が一緒に更新します)。最初に作成されます。
PHP の実行原理と関連記事の詳細については、PHP 中国語 Web サイトに注目してください。