1
0
mirror of https://github.com/php/doc-ja.git synced 2026-03-24 07:02:08 +01:00
Files
archived-doc-ja/language/types/string.xml
2026-02-24 23:35:38 +09:00

1409 lines
45 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<!-- EN-Revision: 2832df2e1bd7daa1ec29ffb167dce1c9feb8cc6b Maintainer: takagi Status: ready -->
<!-- CREDITS: hirokawa,shimooka,mumumu -->
<sect1 xml:id="language.types.string">
<title>文字列</title>
<para>
<type>string</type> は、文字が連結されたものです。PHP では、
文字は 1 バイトと同じです。つまり、256 個の異なる文字を使用可能です。
これは、PHP が Unicode をネイティブにサポートしていないことも意味します。
<link linkend="language.types.string.details">文字列型の詳細</link>を参照ください。
</para>
<note>
<simpara>
32bit ビルドでは、
文字列の最大長は 2GB (2147483647 バイト) です。
</simpara>
</note>
<sect2 xml:id="language.types.string.syntax">
<title>構文</title>
<para>
文字列リテラルは、4 つの異なる方法で指定することが可能です。
</para>
<itemizedlist>
<listitem>
<simpara>
<link linkend="language.types.string.syntax.single">引用符</link>
</simpara>
</listitem>
<listitem>
<simpara>
<link linkend="language.types.string.syntax.double">二重引用符</link>
</simpara>
</listitem>
<listitem>
<simpara>
<link linkend="language.types.string.syntax.heredoc">ヒアドキュメント構文</link>
</simpara>
</listitem>
<listitem>
<simpara>
<link linkend="language.types.string.syntax.nowdoc">Nowdoc 構文</link>
</simpara>
</listitem>
</itemizedlist>
<sect3 xml:id="language.types.string.syntax.single">
<title>引用符</title>
<para>
文字列を指定する最も簡単な方法は、引用符 (文字
<literal>'</literal>) で括ることです。
</para>
<para>
引用符をリテラルとして指定するには、バックスラッシュ
(<literal>\</literal>) でエスケープする必要があります。
バックスラッシュをリテラルとして指定するには、二重
(<literal>\\</literal>) にします。
それ以外の場面で登場するバックスラッシュは、すべてバックスラッシュそのものとして扱われます。
つまり、<literal>\r</literal><literal>\n</literal>
といったおなじみのエスケープシーケンスを書いても特別な効果は得られず、
書いたままの形式で出力されます。
</para>
<note>
<simpara>
<link linkend="language.types.string.syntax.double">ダブルクォート</link> 構文や
<link linkend="language.types.string.syntax.heredoc">ヒアドキュメント構文</link> とは異なり、
<link linkend="language.variables">変数</link>と特殊文字のエスケープシーケンスは、
引用符 (シングルクオート) で括られた文字列にある場合には展開<emphasis>されません</emphasis>
</simpara>
</note>
<example>
<title>Syntax Variants</title>
<programlisting role="php">
<![CDATA[
<?php
echo 'this is a simple string', PHP_EOL;
echo 'You can also have embedded newlines in
strings this way as it is
okay to do', PHP_EOL;
// 出力: Arnold once said: "I'll be back"
echo 'Arnold once said: "I\'ll be back"', PHP_EOL;
// 出力: You deleted C:\*.*?
echo 'You deleted C:\\*.*?', PHP_EOL;
// 出力: You deleted C:\*.*?
echo 'You deleted C:\*.*?', PHP_EOL;
// 出力: This will not expand: \n a newline
echo 'This will not expand: \n a newline', PHP_EOL;
// 出力: Variables do not $expand $either
echo 'Variables do not $expand $either', PHP_EOL;
?>
]]>
</programlisting>
</example>
</sect3>
<sect3 xml:id="language.types.string.syntax.double">
<title>二重引用符</title>
<para>
文字列が二重引用符 (<literal>"</literal>) で括られた場合、
PHP は、以下のエスケープシーケンスを特殊な文字として解釈します。
</para>
<table>
<title>エスケープされた文字</title>
<tgroup cols="2">
<thead>
<row>
<entry>記述</entry>
<entry>意味</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>\n</literal></entry>
<entry>ラインフィード (LF またはアスキーの 0x0A (10))</entry>
</row>
<row>
<entry><literal>\r</literal></entry>
<entry>キャリッジリターン (CR またはアスキーの 0x0D (13))</entry>
</row>
<row>
<entry><literal>\t</literal></entry>
<entry>水平タブ (HT またはアスキーの 0x09 (9))</entry>
</row>
<row>
<entry><literal>\v</literal></entry>
<entry>垂直タブ (VT またはアスキーの 0x0B (11))</entry>
</row>
<row>
<entry><literal>\e</literal></entry>
<entry>エスケープ (ESC あるいはアスキーの 0x1B (27))</entry>
</row>
<row>
<entry><literal>\f</literal></entry>
<entry>フォームフィード (FF またはアスキーの 0x0C (12))</entry>
</row>
<row>
<entry><literal>\\</literal></entry>
<entry>バックスラッシュ</entry>
</row>
<row>
<entry><literal>\$</literal></entry>
<entry>ドル記号</entry>
</row>
<row>
<entry><literal>\"</literal></entry>
<entry>二重引用符</entry>
</row>
<row>
<entry><literal>\[0-7]{1,3}</literal></entry>
<entry>
8進数: 正規表現 <literal>[0-7]{1,3}</literal> にマッチする文字シーケンスは、8 進数表記の 1 文字 (例:. <literal>"\101" === "A"</literal>) です。
正規表現にマッチする文字シーケンスは、8 進数表記の 1 文字です。
1 バイトに収まらない部分は、何もメッセージを出さずにオーバーフローします
(例: <literal>"\400" === "\000"</literal>) 。
</entry>
</row>
<row>
<entry><literal>\x[0-9A-Fa-f]{1,2}</literal></entry>
<entry>
16進数: 正規表現 <literal>[0-9A-Fa-f]{1,2}</literal> にマッチする文字シーケンスは、16 進数表記の 1 文字(例: <literal>"\x41" === "A"</literal>)です。
</entry>
</row>
<row>
<entry><literal>\u{[0-9A-Fa-f]+}</literal></entry>
<entry>
Unicode: 正規表現 <literal>[0-9A-Fa-f]+</literal> にマッチする文字シーケンスは、Unicode のコードポイントです。
そのコードポイントの UTF-8 表現を文字列として出力します。
シーケンスを波括弧で囲む必要があります。例 <literal>"\u{41}" === "A"</literal>
</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
繰り返しますが、この他の文字をエスケープしようとした場合には、
バックスラッシュも出力されます!
</para>
<para>
しかし、二重引用符で括られた文字列で最も重要なのは、
変数名が展開されるところです。詳細は、<link
linkend="language.types.string.parsing">文字列の補間</link>を参照ください。
</para>
</sect3>
<sect3 xml:id="language.types.string.syntax.heredoc">
<title>ヒアドキュメント</title>
<simpara>
文字列を区切る別の方法としてヒアドキュメント構文 ("&lt;&lt;&lt;")
があります。この場合、ある ID (と、それに続けて改行文字)
<literal>&lt;&lt;&lt;</literal>
の後に指定し、文字列を置いた後で、
同じ ID (終端ID) を括りを閉じるために置きます。
</simpara>
<simpara>
終端ID は、スペースまたはタブでインデントできます。
その場合、インデントされた部分は文字列の全ての行から取り除かれます。
PHP 7.3.0 より前のバージョンでは、
終端ID はその行の最初のカラムから始めなければ
<emphasis>いけませんでした</emphasis>
</simpara>
<simpara>
また、終端ID は、PHP の他のラベルと同様の命名規則に従う必要があります。
つまり、英数字およびアンダースコアのみを含み、
数字でない文字またはアンダースコアで始まる必要があります。
</simpara>
<example>
<title>PHP 7.3.0 以降での、基本的なヒアドキュメントの使い方</title>
<programlisting role="php">
<![CDATA[
<?php
// 終端IDをインデントしない場合
echo <<<END
a
b
c
\n
END;
// 終端IDを4つのスペースでインデントする場合
echo <<<END
a
b
c
END;
]]>
</programlisting>
&example.outputs.73;
<screen>
<![CDATA[
a
b
c
a
b
c
]]>
</screen>
</example>
<simpara>
終端ID が、文字列のいずれかの行より奥にインデントされている場合、
<classname>ParseError</classname> がスローされます。
</simpara>
<example>
<title>終端ID は 文字列本体よりも奥にインデントしてはいけない</title>
<programlisting role="php">
<![CDATA[
<?php
echo <<<END
a
b
c
END;
]]>
</programlisting>
&example.outputs.73;
<screen>
<![CDATA[
Parse error: Invalid body indentation level (expecting an indentation level of at least 3) in example.php on line 4
]]>
</screen>
</example>
<simpara>
終端ID をインデントする場合、
インデントに使う文字として、タブまたはスペースが使えます。
しかし、終端ID、および (終端ID までの)文字列の本体どちらであっても、
インデントする際にタブとスペースを混ぜては
<emphasis>いけません</emphasis>
混ぜた場合、
<classname>ParseError</classname> がスローされます。
インデントに使う文字に制限があるのは、
タブとスペースを混ぜてしまうと可読性が損なわれるためです。
</simpara>
<example>
<title>文字列本体 や 終端ID のインデントに違う文字を使う</title>
<programlisting role="php" annotations="non-interactive">
<![CDATA[
<?php
// 以下のコードはいずれも動作しません。
// 文字列本体(スペース) と 終端ID(タブ) とで、異なる文字でインデントする
{
echo <<<END
a
END;
}
// 文字列本体に、タブとスペースを混ぜてインデントする
{
echo <<<END
a
END;
}
// 終端IDのインデントに、スペースとタブを混ぜる
{
echo <<<END
a
END;
}
]]>
</programlisting>
&example.outputs.73;
<screen>
<![CDATA[
Parse error: Invalid indentation - tabs and spaces cannot be mixed in example.php line 8
]]>
</screen>
</example>
<simpara>
文字列本体の後に置かれる 終端ID の後に、
セミコロンや改行を続ける必要はありません。
たとえば、次のようなコードが PHP 7.3.0 以降で動作します:
</simpara>
<example>
<title>終端ID の後に式を継続する</title>
<programlisting role="php">
<![CDATA[
<?php
$values = [<<<END
a
b
c
END, 'd e f'];
var_dump($values);
]]>
</programlisting>
&example.outputs.73;
<screen>
<![CDATA[
array(2) {
[0] =>
string(11) "a
b
c"
[1] =>
string(5) "d e f"
}
]]>
</screen>
</example>
<warning>
<simpara>
終端ID が行のはじめに見つかった場合、
それが別の単語の一部かどうかにかかわらず、
それが終端IDと見なされ、
<classname>ParseError</classname> が起きる可能性があります。
</simpara>
<example>
<title>文字列本体に 終端ID が含まれると、ParseError が起きがち</title>
<programlisting role="php">
<![CDATA[
<?php
$values = [<<<END
a
b
END ING
END, 'd e f'];
]]>
</programlisting>
&example.outputs.73;
<screen>
<![CDATA[
Parse error: syntax error, unexpected identifier "ING", expecting "]" in example.php on line 5
]]>
</screen>
</example>
<simpara>
この問題を避けるために、
次のようなシンプルなルールに従っておくと安全です:
<emphasis>
文字列本体に出現するテキストを、終端ID として採用しない
</emphasis>
</simpara>
</warning>
<warning>
<simpara>
PHP 7.3.0 より前のバージョンで注意すべき非常に重要な点は、
終端ID がある行には、セミコロン
(<literal>;</literal>)
以外の他の文字が含まれていてはいけなかったことです。
これは、特に ID はインデントしてはならないということ、
セミコロンの前に空白やタブを付けてはいけないことを意味していました。
終端ID の前の最初の文字は、使用するオペレーティングシステムで定義された
改行である必要があることにも注意を要します。
これは、UNIX システムでは macOS を含め <literal>\n</literal> となります。
最後の区切り文字の後にもまた、改行を入れる必要があります。
</simpara>
<simpara>
この規則が破られて 終端ID が "clean" でない場合、
終端ID と認識されず、PHP はさらに終端 ID を探し続けます。
適当な 終端ID がみつからない場合、
スクリプトの最終行でパースエラーが発生します。
</simpara>
<example>
<title>PHP 7.3.0 より前のバージョンでの間違った例</title>
<programlisting role="php">
<!-- This is an INVALID example -->
<![CDATA[
<?php
class foo {
public $bar = <<<EOT
bar
EOT;
}
// 識別子はインデントしてはいけません
?>
]]>
</programlisting>
</example>
<example>
<title>PHP 7.3.0 より前のバージョンでも有効な例</title>
<programlisting role="php">
<!-- This is a VALID example -->
<![CDATA[
<?php
class foo {
public $bar = <<<EOT
bar
EOT;
}
?>
]]>
</programlisting>
</example>
<para>
変数を含んでいるヒアドキュメントは、クラスのプロパティの初期化に用いることはできません。
</para>
</warning>
<para>
ヒアドキュメントは二重引用符を使用しませんが、
二重引用符で括られた文字列と全く同様に動作します。
これはつまり、引用符をエスケープする必要はないが、
上記のリストにあるエスケープされたコードは同様に使用できるということです。
変数は展開されますが、文字列の場合と同様に
ヒアドキュメントの内部で複雑な変数を表わす場合には注意が必要です。
</para>
<example>
<title>ヒアドキュメントで文字列を括る例</title>
<programlisting role="php">
<![CDATA[
<?php
$str = <<<EOD
Example of string
spanning multiple lines
using heredoc syntax.
EOD;
/* 変数を使用するより複雑な例 */
class foo
{
var $foo;
var $bar;
function __construct()
{
$this->foo = 'Foo';
$this->bar = array('Bar1', 'Bar2', 'Bar3');
}
}
$foo = new foo();
$name = 'MyName';
echo <<<EOT
My name is "$name". I am printing some $foo->foo.
Now, I am printing some {$foo->bar[1]}.
This should print a capital 'A': \x41
EOT;
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
My name is "MyName". I am printing some Foo.
Now, I am printing some Bar2.
This should print a capital 'A': A]]>
</screen>
</example>
<para>
ヒアドキュメント構文を用いて、
関数の引数にデータを渡すこともできます。
</para>
<example>
<title>ヒアドキュメントを引数に使用する例</title>
<programlisting role="php">
<![CDATA[
<?php
var_dump(array(<<<EOD
foobar!
EOD
));
?>
]]>
</programlisting>
</example>
<para>
static変数やクラスのプロパティ/定数は、
ヒアドキュメント構文で初期化することができます。
</para>
<example>
<title>ヒアドキュメントを用いた静的な値の初期化</title>
<programlisting role="php" annotations="non-interactive">
<![CDATA[
<?php
// static 変数
function foo()
{
static $bar = <<<LABEL
Nothing in here...
LABEL;
}
// クラスのプロパティ/定数
class foo
{
const BAR = <<<FOOBAR
Constant example
FOOBAR;
public $baz = <<<FOOBAR
Property example
FOOBAR;
}
?>
]]>
</programlisting>
</example>
<para>
ヒアドキュメントの宣言をダブルクォートで囲むこともできます。
</para>
<example>
<title>ヒアドキュメントでのダブルクォート</title>
<programlisting role="php">
<![CDATA[
<?php
echo <<<"FOOBAR"
Hello World!
FOOBAR;
?>
]]>
</programlisting>
</example>
</sect3>
<sect3 xml:id="language.types.string.syntax.nowdoc">
<title>Nowdoc</title>
<para>
Nowdoc はヒアドキュメントと似ていますが、
ヒアドキュメントがダブルクォートで囲んだ文字列として扱われるのに対して、
Nowdoc はシングルクォートで囲んだ文字列として扱われます。
Nowdoc の使用方法はヒアドキュメントとほぼ同じですが、
その中身について <emphasis>文字列の補間処理を行いません</emphasis>
PHP のコードや大量のテキストを埋め込む際に、
エスケープが不要になるので便利です。この機能は、SGML の
<literal>&lt;![CDATA[ ]]&gt;</literal>
(ブロック内のテキストをパースしないことを宣言する)
と同じようなものです。
</para>
<para>
Nowdoc の書き方は、ヒアドキュメントと同じように
<literal>&lt;&lt;&lt;</literal> を使用します。
しかし、その後に続く識別子をシングルクォートで囲んで
<literal>&lt;&lt;&lt;'EOT'</literal> のようにします。
ヒアドキュメントの識別子に関する決まりがすべて Nowdoc
の識別子にも当てはまります。特に 終端ID の書き方に関する決まりに注意しましょう。
</para>
<example>
<title>Nowdoc による文字列のクォートの例</title>
<programlisting role="php">
<![CDATA[
<?php
echo <<<'EOD'
Example of string spanning multiple lines
using nowdoc syntax. Backslashes are always treated literally,
e.g. \\ and \'.
EOD;
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Example of string spanning multiple lines
using nowdoc syntax. Backslashes are always treated literally,
e.g. \\ and \'.
]]>
</screen>
</example>
<example>
<title>変数がある場合の、Nowdoc による文字列のクォートの例</title>
<programlisting role="php">
<![CDATA[
<?php
/* 変数を使った、より複雑な例 */
class foo
{
public $foo;
public $bar;
function __construct()
{
$this->foo = 'Foo';
$this->bar = array('Bar1', 'Bar2', 'Bar3');
}
}
$foo = new foo();
$name = 'MyName';
echo <<<'EOT'
My name is "$name". I am printing some $foo->foo.
Now, I am printing some {$foo->bar[1]}.
This should not print a capital 'A': \x41
EOT;
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
My name is "$name". I am printing some $foo->foo.
Now, I am printing some {$foo->bar[1]}.
This should not print a capital 'A': \x41]]>
</screen>
</example>
<example>
<title>静的なデータの例</title>
<programlisting role="php" annotations="non-interactive">
<![CDATA[
<?php
class foo {
public $bar = <<<'EOT'
bar
EOT;
}
?>
]]>
</programlisting>
</example>
</sect3>
<sect3 xml:id="language.types.string.parsing">
<title>文字列の補間処理</title>
<simpara>
文字列が二重引用符で括られるかヒアドキュメントで指定された場合、
その中の<link linkend="language.variables">変数</link>は置き換えられます。
</simpara>
<simpara>
構文の型には、<link
linkend="language.types.string.parsing.basic">単純な</link>構文と
<link linkend="language.types.string.parsing.advanced">高度な
</link>構文の 2 種類があります。単純な構文は、最も一般的で便利です。
この構文では、変数や配列の値、オブジェクトのプロパティを、
簡単に文字列に埋め込むことができます。
</simpara>
<sect4 xml:id="language.types.string.parsing.basic">
<title>単純な構文</title>
<simpara>
ドル記号 (<literal>$</literal>) を見付けると、
その後に変数名で使える文字は変数として解釈され、置き換えられます。
</simpara>
<example>
<title>文字列の置き換え</title>
<programlisting role="php">
<![CDATA[
<?php
$juice = "apple";
echo "He drank some $juice juice." . PHP_EOL;
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
He drank some apple juice.
]]>
</screen>
</example>
<simpara>
形式的には、文字列の変数の置き換えの文法は下記のようになります:
</simpara>
<informalexample>
<programlisting>
<![CDATA[
string-variable::
variable-name (offset-or-property)?
| ${ expression }
offset-or-property::
offset-in-string
| property-in-string
offset-in-string::
[ name ]
| [ variable-name ]
| [ integer-literal ]
property-in-string::
-> name
variable-name::
$ name
name::
[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*
]]>
</programlisting>
</informalexample>
<warning>
<para>
<literal>${ expression }</literal> の形式は、
PHP 8.2.0 以降では推奨されなくなりました。
なぜなら、これは
<link linkend="language.variables.variable">可変変数</link>:
として解釈される可能性があるからです。
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
const foo = 'bar';
$foo = 'foo';
$bar = 'bar';
var_dump("${foo}");
var_dump("${(foo)}");
?>
]]>
</programlisting>
&example.outputs.82;
<screen>
<![CDATA[
Deprecated: Using ${var} in strings is deprecated, use {$var} instead in file on line 6
Deprecated: Using ${expr} (variable variables) in strings is deprecated, use {${expr}} instead in file on line 9
string(3) "foo"
string(3) "bar"
]]>
</screen>
&example.outputs;
<screen>
<![CDATA[
string(3) "foo"
string(3) "bar"
]]>
</screen>
</informalexample>
<link linkend="language.types.string.parsing.advanced">高度な</link>
文字列補間の記法を代わりに使うべきです。
</para>
</warning>
<note>
<simpara>
有効な変数名にならない場合、ドル記号は文字列中でそのまま解釈されます:
</simpara>
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
echo "No interpolation $ has happened\n";
echo "No interpolation $\n has happened\n";
echo "No interpolation $2 has happened\n";
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
No interpolation $ has happened
No interpolation $
has happened
No interpolation $2 has happened
]]>
</screen>
</informalexample>
</note>
<example>
<title>配列の最初の次元やプロパティの値を補間させる</title>
<programlisting role="php">
<![CDATA[
<?php
$juices = array("apple", "orange", "string_key" => "purple");
echo "He drank some $juices[0] juice.";
echo PHP_EOL;
echo "He drank some $juices[1] juice.";
echo PHP_EOL;
echo "He drank some $juices[string_key] juice.";
echo PHP_EOL;
class A {
public $s = "string";
}
$o = new A();
echo "Object value: $o->s.";
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
He drank some apple juice.
He drank some orange juice.
He drank some purple juice.
Object value: string.
]]>
</screen>
</example>
<note>
<simpara>
配列のキーについては、クォートを外さなければなりません。
よって、単純な記法では定数をキーとして参照できません。
<link linkend="language.types.string.parsing.advanced">高度な</link>
記法を使ってください。
</simpara>
</note>
<simpara>
PHP 7.1.0 以降では、<emphasis>負の</emphasis>
数値インデックスもサポートされています
</simpara>
<example><title>負の数値インデックス</title>
<programlisting role="php">
<![CDATA[
<?php
$string = 'string';
echo "The character at index -2 is $string[-2].", PHP_EOL;
$string[-3] = 'o';
echo "Changing the character at index -3 to o gives $string.", PHP_EOL;
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
The character at index -2 is n.
Changing the character at index -3 to o gives strong.
]]>
</screen>
</example>
<simpara>
さらに複雑な処理は、
<link linkend="language.types.string.parsing.advanced">高度な</link>
記法を使う必要があります。
</simpara>
</sect4>
<sect4 xml:id="language.types.string.parsing.advanced">
<title>高度な (波括弧) 記法</title>
<simpara>
高度な波括弧を使った記法を使うと、
任意のアクセス方法で <emphasis>変数</emphasis>
の補間を行うことができます。
</simpara>
<simpara>
任意のスカラー変数や、配列の要素、オブジェクトのプロパティ
(<modifier>static</modifier> かそうでないかは問いません)
の文字列表現を、この記法に含めることができます。
式は文字列の外部に現れるものと同じやり方で書くことができ、
<literal>{</literal><literal>}</literal> で囲みます。
<literal>{</literal> はエスケープできないので、
この記法は <literal>{</literal> のすぐ後に <literal>$</literal>
が続く場合にのみ認識されます。
<literal>{$</literal> を使いたい場合は
<literal>{\$</literal> と書くようにしてください。
以下の複数の例を見ると、わかりやすいでしょう:
</simpara>
<example>
<title>{} による記法</title>
<programlisting role="php">
<![CDATA[
<?php
const DATA_KEY = 'const-key';
$great = 'fantastic';
$arr = [
'1',
'2',
'3',
[41, 42, 43],
'key' => 'Indexed value',
'const-key' => 'Key with minus sign',
'foo' => ['foo1', 'foo2', 'foo3']
];
// Won't work, outputs: This is { fantastic}
echo "This is { $great}";
// Works, outputs: This is fantastic
echo "This is {$great}";
class Square {
public $width;
public function __construct(int $width) { $this->width = $width; }
}
$square = new Square(5);
// Works
echo "This square is {$square->width}00 centimeters wide.";
// Works, quoted keys only work using the curly brace syntax
echo "This works: {$arr['key']}";
// Works
echo "This works: {$arr[3][2]}";
echo "This works: {$arr[DATA_KEY]}";
// When using multidimensional arrays, always use braces around arrays
// when inside of strings
echo "This works: {$arr['foo'][2]}";
echo "This works: {$obj->values[3]->name}";
echo "This works: {$obj->$staticProp}";
// Won't work, outputs: C:\directory\{fantastic}.txt
echo "C:\directory\{$great}.txt";
// Works, outputs: C:\directory\fantastic.txt
echo "C:\\directory\\{$great}.txt";
?>
]]>
</programlisting>
</example>
<note>
<simpara>
この記法を使うと任意の式が使えるので、
<link linkend="language.variables.variable">可変変数</link>
を使うこともできます。
</simpara>
</note>
</sect4>
</sect3>
<sect3 xml:id="language.types.string.substr">
<title>文字列への文字単位のアクセスと修正</title>
<para>
<varname>$str[42]</varname> のように、
角括弧を使用してゼロから始まるオフセットを指定すると、
文字列内の任意の文字にアクセスし、修正することが可能です。
つまり、文字列を文字の配列として考えるわけです。
複数の文字を取り出したり変更したりしたいときは、関数
<function>substr</function> および <function>substr_replace</function>
が使えます。
</para>
<note>
<simpara>
PHP 7.1.0 以降では、負の文字列オフセットにも対応するようになりました。
これは、文字列の末尾からのオフセットを表します。
以前のバージョンでは、負のオフセットで読み込もうとすると <constant>E_NOTICE</constant>
が発生し (空文字列を返します)、負のオフセットで書き込もうとすると <constant>E_WARNING</constant>
が発生していました (文字列には何も手が加えられません)。
</simpara>
</note>
<note>
<simpara>
PHP 8.0.0 より前のバージョンでは、
<varname>$str{42}</varname>
のように波括弧を使用してアクセスすることもできました。
この文法は PHP 7.4.0 以降は非推奨になり、
PHP 8.0.0 以降はサポートされなくなっています。
</simpara>
</note>
<warning>
<simpara>
範囲外のオフセットに書き込んだ場合は、その地点まで空白文字で埋められます。
整数型以外の型は整数型に変換されます。
無効なオフセット形式を指定した場合は <constant>E_WARNING</constant> を発行します。
文字列を代入した場合は最初の文字だけを使用します。
PHP 7.1.0 以降では、空の文字列を代入すると 致命的なエラーが発生するようになりました。
これまでのバージョンでは、NULL バイトが代入されていました。
</simpara>
</warning>
<warning>
<simpara>
内部的には、PHP の文字列はバイト配列です。
そのため、角括弧を使った配列形式での文字列へのアクセスは、
マルチバイト対応ではありません。この方法は、
ISO-8859-1 のようなシングルバイトエンコーディングの文字列に対してだけしか使えません。
</simpara>
</warning>
<note>
<simpara>
PHP 7.1.0 以降では、空の文字列に空のインデックス演算子を適用すると
致命的なエラーが発生するようになりました。
これまでのバージョンではエラーにならず、空の文字列が配列に変換されていました。
</simpara>
</note>
<example>
<title>文字列の例</title>
<programlisting role="php">
<![CDATA[
<?php
// 文字列の最初の文字を取得します
$str = 'This is a test.';
$first = $str[0];
var_dump($first);
// 文字列の 3 番目の文字を取得します
$third = $str[2];
var_dump($third);
// 文字列の最後の文字を取得します
$str = 'This is still a test.';
$last = $str[strlen($str)-1];
var_dump($last);
// 文字列の最後の文字を変更します
$str = 'Look at the sea';
$str[strlen($str)-1] = 'e';
var_dump($str);
?>
]]>
</programlisting>
</example>
<para>
文字列のオフセットは整数あるいは整数と見なせる文字列に限られます。
それ以外の場合は警告が発生します。
</para>
<example>
<title>不正な文字列のオフセットの例</title>
<programlisting role="php">
<![CDATA[
<?php
$str = 'abc';
$keys = [ '1', '1.0', 'x', '1x' ];
foreach ($keys as $keyToTry) {
var_dump(isset($str[$keyToTry]));
try {
var_dump($str[$keyToTry]);
} catch (TypeError $e) {
echo $e->getMessage(), PHP_EOL;
}
echo PHP_EOL;
}
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
bool(true)
string(1) "b"
bool(false)
Cannot access offset of type string on string
bool(false)
Cannot access offset of type string on string
bool(false)
Warning: Illegal string offset "1x" in Standard input code on line 10
string(1) "b"
]]>
</screen>
</example>
<note>
<para>
その他の型の変数
(配列や、適切なインターフェイスを実装したオブジェクトを除く)
に対して <literal>[]</literal><literal>{}</literal>
でアクセスすると、何もメッセージを出さずに単に &null; を返します。
</para>
</note>
<note>
<para>
文字列リテラル内の文字に対して
<literal>[]</literal><literal>{}</literal> でアクセスすることができます。
</para>
</note>
<note>
<para>
文字列リテラル内の文字に対して、
<literal>{}</literal> を使ってアクセスする機能は、
PHP 7.4 で非推奨になり、PHP 8.0 で削除されました。
</para>
</note>
</sect3>
</sect2><!-- end syntax -->
<sect2 xml:id="language.types.string.useful-funcs">
<title>便利な関数および演算子</title>
<para>
文字列は、'.' (ドット) 結合演算子で結合することが可能です。'+'
(加算) 演算子はこの例では出てこないことに注意してください。詳細については
<link linkend="language.operators.string">文字列演算子</link>
を参照ください。
</para>
<para>
文字列の修正を行う場合には、便利な関数がたくさん用意されています。
</para>
<simpara>
一般的な関数については、<link linkend="ref.strings">文字列関数の節</link>
を参照ください。高度な検索/置換を行う関数については
<link linkend="ref.pcre">Perl 互換の正規表現関数</link> を参照ください。
</simpara>
<simpara>
<link linkend="ref.url">URL 文字列用関数</link>や文字列の暗号化/
復号用の関数 (<link linkend="ref.sodium">Sodium</link> および
<link linkend="ref.hash">Hash</link>) もあります。
</simpara>
<simpara>
最後に、探しているものがまだ見付からない場合には、
<link linkend="ref.ctype">文字型の関数</link>も参照ください。
</simpara>
</sect2>
<sect2 xml:id="language.types.string.casting">
<title>文字列への変換</title>
<para>
<literal>(string)</literal> キャストや <function>strval</function>
関数を使って変数を文字列へ変換することができます。
文字列型を必要とする式のスコープにおいて、文字列への変換は自動的に行われます。
<function>echo</function><function>print</function> 関数を使うとき、
あるいは変数を文字列と比較するときにこの自動変換が行われます。
マニュアルの<link linkend="language.types"></link>
<link linkend="language.types.type-juggling">型の相互変換</link>
の項を読むとわかりやすいでしょう。
<function>settype</function>も参照ください。
</para>
<para>
<type>bool</type>&true; は文字列の <literal>"1"</literal> に、
&false;<literal>""</literal> (空文字列) に変換されます。
これにより boolean と文字列の値を相互に変換することができます。
</para>
<para>
<type>int</type> (整数) や浮動小数点数 (<type>float</type>) は
その数値の数字として文字列に変換されます (指数の表記や浮動小数点数を含めて)。
浮動小数点数は、指数表記
(<literal>4.1E+6</literal>) を使用して変換されます。
</para>
<note>
<para>
PHP 8.0.0 以降では、小数点を表す文字は常にピリオド ("<literal>.</literal>") です。
それより前のバージョンでは、
スクリプトのロケール (LC_NUMERIC カテゴリ)
によって決まります。
<function>setlocale</function> を参照ください。
</para>
</note>
<para>
配列は常に <literal>"Array"</literal> という文字列に変換されるので、
<type>array</type> の中を見るために <function>echo</function>
<function>print</function> を使ってダンプさせることはできません。
一つの要素を見るためには、<literal>echo $arr['foo']</literal>
のようにしてください。内容の全てをダンプ/見るためには以降の TIP をご覧ください。
</para>
<para>
<type>object</type><type>string</type> へ変換するには、
マジック・メソッド <link linkend="language.oop5.magic">__toString</link> を使用してください。
</para>
<para>
リソースは常に <literal>"Resource id #1"</literal>
という文字列に変換されます。<literal>1</literal> は実行中の
PHP によって割り当てられる
<type>resource</type> の番号です。
この文字列の構造に依存したコードを書いてはいけません (この構造は変わる可能性があります)
が、スクリプトの実行中 (ウェブのリクエストや CLI プロセスの処理中) は、指定したリソースに対してこの文字列が一意に割り当てられることが保証されます。
他のリソースで同じ文字列が再利用されることはありません。
リソースの型を知るためには <function>get_resource_type</function>
を使用してください。
</para>
<para>
&null; は常に空文字列に変換されます。
</para>
<para>
以上に述べたように、配列、オブジェクト、リソースをプリントアウトしても
その値に関する有益な情報を得られるわけではありません。
デバッグのために値を出力するのによりよい方法が知りたければ、
<function>print_r</function>
<function>var_dump</function> を参照ください。
</para>
<para>
PHP 変数を恒久的に保存するための文字列に変換することもできます。
この方法はシリアライゼーションと呼ばれ、
<function>serialize</function> 関数によって実現できます。
</para>
</sect2>
<sect2 xml:id="language.types.string.details">
<title>文字列型の詳細</title>
<para>
PHP における文字列型は、バイトの配列と整数値 (バッファ長) で実装されています。
バイト列を文字列に変換する方法については何の情報も持っておらず、完全にプログラマ任せとなっています。
文字列を構成する値には何の制限もありません。特に気をつけるべきなのは、
<literal>0</literal> のバイト (いわゆる “NUL バイト”) を文字列内のどの部分にでも使えるという点です
(しかし、このマニュアル上で「バイナリセーフではない」とされている一部の関数では、
受け取った文字列をライブラリに渡すときに NUL バイト以降を無視することがあります)。
</para>
<para>
PHP の文字列型の正体を知ってしまえば、なぜ PHP には「バイト型」が存在しないのかもわかります。
つまり、文字列型がその役割を受け持っているのです。テキスト以外のデータ、
たとえばネットワークソケットから読み込んだ任意のデータを返す関数も、
文字列で値を返します。
</para>
<para>
PHP が文字列に対して特定のエンコーディングを強制しないのなら、
いったいどのようにして文字列リテラルをエンコードしているのでしょう?
たとえば、文字列 <literal>"á"</literal> と同等なのは <literal>"\xE1"</literal> (ISO-8859-1)、
<literal>"\xC3\xA1"</literal> (UTF-8, C form)、
<literal>"\x61\xCC\x81"</literal> (UTF-8, D form) のどれでしょう?
あるいはそれ以外の何かなのでしょうか?
実は、文字列のエンコードはスクリプトファイルのエンコード方式に従って行われるというのが正解です。
したがって、もしスクリプトが ISO-8859-1 で書かれているのなら、文字列も ISO-8859-1
でエンコードされます。その他のエンコードの場合も同様です。
しかし、Zend Multibyte が有効になっている場合は話が別です。
この場合は、スクリプトはどんなエンコーディングで書いてもかまいません
(明示的に宣言することもできるし、自動検出させることもできます)。
スクリプトはその後で内部エンコーディングに変換されるので、
文字列リテラルも内部エンコーディングと同じ方式で符号化されます。
スクリプトのエンコーディング (あるいは、Zend Multibyte
を有効にした場合の内部エンコーディング) には、一部制限があることに注意しましょう。
ひとことで言うと、ASCII の上位互換 でなければならないということです。
UTF-8 や ISO-8859-1 などがこれにあたります。
しかし、状態に依存する
(たとえば、同じバイト値が、先頭にあるときとシフト状態にあるときで違う意味になる)
エンコーディングは、問題になる可能性があります。
</para>
<para>
もちろん、利便性を考慮すれば、テキストを操作する関数が文字列のエンコードを扱う際に
何らかの前提に基づかざるを得ないこともあります。
残念ながら、PHP の各関数が文字列のエンコーディングを判断する方法はまったく統一されていません。
</para>
<itemizedlist>
<listitem>
<simpara>
いくつかの関数は、文字列が何らかのシングルバイトエンコーディングで符号化されているものと見なします。
しかし、文字列内の各バイトが必ずしも特定の文字に変換できなくてもかまいません。
このタイプの関数は、<function>substr</function>
<function>strpos</function><function>strlen</function>
<function>strcmp</function> などです。
これらの関数については、文字列を扱うというよりメモリ上のバッファを扱うものととらえてもよいでしょう。
つまり、バイト列とバイトオフセットで考えるということです。
</simpara>
</listitem>
<listitem>
<simpara>
文字列のエンコーディングを受け取る関数もあります。
エンコーディング情報を省略したときにはデフォルトを用意していることもあるでしょう。
このタイプの関数の例は、<function>htmlentities</function>
大半の <link linkend="book.mbstring">mbstring</link> 関数です。
</simpara>
</listitem>
<listitem>
<simpara>
現在のロケール (<function>setlocale</function> を参照ください) を使うけれども、
処理はバイト単位で行う関数もあります。
</simpara>
</listitem>
<listitem>
<simpara>
最後に、文字列が特定のエンコーディング (たいていは UTF-8)
であることを前提としている関数があります。
<link linkend="book.intl">intl</link> の関数や
<link linkend="book.pcre">PCRE</link> の関数
(<literal>u</literal> 修飾子を使う場合のみ)
の多くがこのタイプになります。
</simpara>
</listitem>
</itemizedlist>
<para>
結局、Unicode を使うプログラムをきちんと書くには、
うまく動かない関数を使わないよう注意するしかないということです。
特にデータを破壊してしまう可能性のある関数の使用は避け、
きちんと動作する関数を使うようにしましょう。
<link linkend="book.intl">intl</link>
<link linkend="book.mbstring">mbstring</link>
の関数を選択するとよいでしょう。
しかし、Unicode をまともに扱える関数を使うというのは単なる始まりに過ぎません。
たとえ関数側で Unicode を扱う機能があったとしても、
Unicode の仕様に関する知識は不可欠です。
たとえば、世の中には大文字と小文字しか存在しないという思い込みで作ったプログラムは、
うまく動かない可能性があります。
</para>
</sect2>
</sect1><!-- end string -->
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->