1.前言
PHP(从 PHP 3.05 开端)为保管对象提供了一组序列化和反序列化的函数:serialize、unserialize。不过在 PHP
手册中对这两个函数的标明仅限于如何运用,而对序列化结果的格式却没做任何标明。因而,这对在其他言语中完成 PHP
方式的序列化来说,就比拟费事了。虽然以前也搜集了一些其他言语完成的 PHP
序列化的顺序,不过这些完成都不完全,当序列化或反序列化一些比拟庞杂的对象时,就会出错了。于是我决议写一份关于 PHP
序列化格式详解的文档(也就是这一篇文档),以便在编写其他言语完成的 php
序列化顺序时能有一个比拟完壁的参考。这篇文章中所写的内容是我议决编写顺序测试和阅读 PHP 源代码得到的,所以,我无法 100%
保证一切的内容都是正确的,不过我会尽量保证我所写下的内容的正确性,关于我还不太清楚的地点,我会在文中明白指出,也期盼群众可以给予补充和完备。
2.概述
PHP 序列化后的内容是容易的文本格式,但是对字母大小写和空白(空格、回车、换行等)敏感,并且字符串是依照字节(或许说是 8
位的字符)计算的,因而,更适宜的说法是 PHP
序列化后的内容是字节流格式。因而用其他言语完成时,假设所完成的言语中的字符串不是字节贮存格式,而是 Unicode
贮存格式的话,序列化后的内容不适宜保管为字符串,而应保管为字节流对象或许字节数组,否则在与 PHP 执行数据交流时会发生错误。
PHP 对不一样类型的数据用不一样的字母执行标示,Yahoo 开发站点提供的 Using Serialized PHP with Yahoo! Web Services 一文中给出一切的字母标示及其意思:
Word-WRAP: break-word" bgColor=#ddedfb>
a - array
b - boolean
d - double
i - integer
o - common object
r - reference
s - string
C - custom object
O - class
N - null
R - pointer reference
U - unicode string
N 表示的是 NULL,而 b、d、i、s 表示的是四种标量类型,现在其它言语所完成的 PHP 序列化顺序基本上都完成了对这些类型的序列化和反序列化,不过有一些完成中对 s (字符串)的完成存在疑问。
a、O 属于最常用的复合类型,大局部其他言语的完成都很好的完成了对 a 的序列化和反序列化,但对 O 只完成了 PHP4 中对象序列化格式,而没有提供对 PHP 5 中扩展的对象序列化格式的支持。
r、R 区分表示对象援用和指针援用,这两个也比拟有用,在序列化比拟庞杂的数组和对象时就会发生带有这两个标示的数据,后面我们将细致解说这两个标示,现在这两个标示尚没有觉察有其他言语的完成。
C 是 PHP5 中引入的,它表示自定义的对象序列化方式,虽然这关于其它言语来说是没有必要完成的,由于很少会用到它,但是后面照旧会对它执行细致解说的。
U 是 PHP6 中才引入的,它表示 Unicode 编码的字符串。由于 PHP6 中提供了 Unicode
方式保管字符串的才干,因而它提供了这种序列化字符串的格式,不过这个类型 PHP5、PHP4
都不支持,而这两个版本现在是主流,因而在其它言语完成该类型时,不推荐用它来执行序列化,不过可以完成它的反序列化流程。在后面我也会对它的格式执行标
明。
开头尚有一个 o,这也是我独一还没弄清楚的一个数据类型标示。这个标示在 PHP3 中被引入用来序列化对象,但是到了 PHP4 现在就被 O
取代了。在 PHP3 的源代码中可以看到对 o 的序列化和反序列化与数组 a 基本上是一样的。但是在 PHP4、PHP5 和 PHP6
的源代码中序列化局部里都找不到它的影子,但是在这多个版本的反序列化顺序源代码中却都有对它的处理,不过把它处理成什么我还没弄清楚。因而对它暂时不再作更多标明了。
3.NULL 和标量类型的序列化
NULL 和标量类型的序列化是最容易的,也是构成契合类型序列化的基本。这局部内容相信许多 PHP 开发者都以前熟知。假设您觉得以前掌握了这局部内容,可以直接跳过这一章。
3.1.NULL 的序列化
在 PHP 中,NULL被序列化为:
N;
3.2.boolean 型数据的序列化
boolean型数据被序列化为:
b:
其中
3.3.integer 型数据的序列化
integer型数据(整数)被序列化为:
i:
其中
2147483647。数字前可以有正负号,假设被序列化的数字超越这个范围,则会被序列化为浮点数类型而不是整型。假设序列化后的数字超越这个范围
(PHP 自身序列化时不会发作这个疑问),则反序列化时,将不会前往希冀的数值。
3.4.double 型数据的序列化
double型数据(浮点数)被序列化为:
d:
其中
中浮点数的范围一样。可以表示成整数方式、浮点数方式和迷信技术法方式。假设序列化无量大数,则
INF,假设序列化负无量大,则
能表示的最大值,则反序列化时前往无量大(INF),假设序列化后的数字范围超越 PHP 所能表示的最小精度,则反序列化时前往 0。
3.5.string 型数据的序列化
string型数据(字符串)被序列化为:
s:
其中
是非负整数,数字前可以带有正号(+)。
255 的字符相对应。每个字符都表示原字符意思,没有转义字符,
是这个字节流的字节个数。
4.容易复合类型的序列化
PHP 中的复合类型有限组(array)和对象(object)两种,本章首要简介在容易情况下这两品种型数据的序列化格式。关于嵌套定义的复合类型和自定义序列化方式的对象的序列化格式将在后面的章节细致讨论。
4.1.数组的序列化
数组(array)通常被序列化为:
a:
其中
表示数组下标,
下标的类型只好是整型或许字符串型,序列化后的格式跟整型和字符串型数据序列化后的格式类似。
数组元素值可以是随意类型,其序列化后的格式与其所对应的类型序列化后的格式类似。
4.2.对象的序列化
对象(object)通常被序列化为:
O:
其中
表示对象中的字段1个数。这些字段包括在对象所在类及其祖先类中用 var、public、protected 和 private
声明的字段,但是不包括 static 和 const 声明的静态字段。也就是说只需实例(instance)字段。
字段名是字符串型,序列化后格式与字符串型数据序列化后的格式类似。
字段值可以是随意类型,其序列化后的格式与其所对应的类型序列化后的格式类似。
但字段名的序列化与它们声明的可见性是相关的,下面重点讨论一下关于字段名的序列化。
4.3.对象字段名的序列化
var 和 public 声明的字段都是公共字段,因而它们的字段名的序列化格式是类似的。公共字段的字段名依照声明时的字段名执行序列化,但序列化后的字段名中不包括声明时的变量前缀符号 $。
protected 声明的字段为维护字段,在所声明的类和该类的子类中可见,但在该类的对象实例中不可见。因而维护字段的字段名在序列化时,字段名先面会加上 \0*\0的前缀。这里的 \0 表示 ASCII 码为 0 的字符,而不是 \0 组合。
private 声明的字段为个体字段,只在所声明的类中可见,在该类的子类和该类的对象实例中均不可见。因而个体字段的字段名在序列化时,字段名先面会加上 \0
字段名被作为字符串序列化时,字符串值中包括依据其可见性所加的前缀。字符串长度也包括所加前缀的长度。其中 \0 字符也是计算长度的。
1注:
在 PHP 手册中,字段被称为属性,而实践上,在 PHP 5 中引入的用 __set、__get 来定义的对象成员更适宜叫做属性。由于用
__set、__get 来定义的对象成员与其它言语中的属性的行为是一致,而 PHP
手册中所说的属性实践上在其他言语中(比如:C#)中被称为字段,为了防止混杂,这里也称为字段,而不是属性。
5.嵌套复合类型的序列化
上一章讨论了容易的复合类型的序列化,群众会觉察关于容易的数组和对象原本也很容易。但是假设遇到自己包括自己或许 A 包括 B,B 又包括 A 这类的对象或数组时,PHP 又该如何序列化这种对象和数组呢?本章我们就来讨论这种情况下的序列化方式。
5.1.对象援用和指针援用
在 PHP 中,标量类型数据是值传递的,而复合类型数据(对象和数组)是援用传递的。但是复合类型数据的援用传递和用 & 符号明白指定的援用传递是有区别的,前者的援用传递是对象援用,然后者是指针援用。
在解释对象援用和指针援用之前,先让我们看多个例子。
<?php echo "<pre class="brush:php;toolbar:false">"; class SampleClass { var $value; } $a = new SampleClass(); $a->value = $a; $b = new SampleClass(); $b->value = &$b; echo serialize($a); echo "\n"; echo serialize($b); echo "\n"; echo ""; ?>
这个例子的输出结果是这样的:
O:11:"SampleClass":1:{s:5:"value";r:1;}
O:11:"SampleClass":1:{s:5:"value";R:1;}
群众会觉察,这里变量 $a 的 value 字段的值被序列化成了 r:1,而 $b 的 value 字段的值被序列化成了 R:1。
但是对象援用和指针援用究竟有什么区别呢?
群众可以看下面这个例子:
echo "<pre class="brush:php;toolbar:false">"; class SampleClass { var $value; } $a = new SampleClass(); $a->value = $a; $b = new SampleClass(); $b->value = &$b; $a->value = 1; $b->value = 1; var_dump($a); var_dump($b); echo "";
群众会觉察,运转结果也许出乎你的意料:
object(SampleClass)#1 (1) {
["value"]=>
int(1)
}
int(1)
改动 $a->value 的值仅仅是改动了 $a->value 的值,而改动 $b->value 的值却改动了 $b 自身,这就是对象援用和指针援用的区别。
不过很不幸的是,PHP 对数组的序列化犯了一个错误,虽然数组自身在传递时也是对象援用传递,但是在序列化时,PHP 似乎遗忘了这一点,看下面的例子:
echo "<pre class="brush:php;toolbar:false">"; $a = array(); $a[1] = 1; $a["value"] = $a; echo $a["value"]["value"][1]; echo "\n"; $a = unserialize(serialize($a)); echo $a["value"]["value"][1]; echo "";
结果是:
1
群众会觉察,将原数组序列化再反序列化后,数组结构变了。原本 $a["value"]["value"][1] 中的值 1,在反序列化之后丧失了。
原由是什么呢?让我们输出序列化之后的结果来看一看:
$a = array(); $a[1] = 1; $a["value"] = $a; echo serialize($a);
结果是:
a:2:{i:1;i:1;s:5:"value";a:2:{i:1;i:1;s:5:"value";N;}}
原来,序列化之后,$a["value"]["value"] 变成了 NULL,而不是一个对象援用。
也就是说,PHP 只对对象在序列化时才会生成对象援用标示(r)。对一切的标量类型和数组(也包括 NULL)序列化时都不会生成对象援用。但是假设明白运用了 & 符号作的援用,在序列化时,会被序列化为指针援用标示(R)。
5.2.援用标示后的数字
在上面的例子中群众能够以前看到了,对象援用(r)和指针援用(R)的格式为:
r:
R:
群众必需很奇异后面这个
这个
我想群众能够还不是很明白,那么我来举例标明一下:
class ClassA { var $int; var $str; var $bool; var $obj; var $pr; } $a = new ClassA(); $a->int = 1; $a->str = "Hello"; $a->bool = false; $a->obj = $a; $a->pr = &$a->str; echo serialize($a);
这个例子的结果是:
O:6:"ClassA":5:{s:3:"int";i:1;s:3:"str";s:5:"Hello";s:4:"bool";b:0;s:3:"obj";r:1;s:2:"pr";R:3;}
在这个例子中,最先序列化的对象是 ClassA 的一个对象,那么给它编号为
1,接下来要序列化的是这个对象的多个成员,第一个被序列化的成员是 int 字段,那它的编号就为 2,接下来被序列化的成员是
str,那它的编号就是 3,依此类推,到了 obj 成员时,它觉察该成员以前被序列化了,并且编号为 1,因而它被序列化时,就被序列化成了
r:1; ,在接下来被序列化的是 pr 成员,它觉察该成员实践上是指向 str 成员的一个援用,而 str 成员的编号为 3,因而,pr
就被序列化为 R:3; 了。
PHP 是如何来编号被序列化的对象的呢?实践上,PHP
在序列化时,最先树立一个空表,然后每个被序列化的对象在被序列化之前,都须要先计算该对象的 Hash 值,然后判别该 Hash
值能无法以前出如今该表中了,假设没有呈现,就把该 Hash 值添加到这个表的开头,前往添加成功。假设呈现了,则前往添加失败,但是在前往失败前先判别该对象能无法是一个援用(用 & 符号定义的援用),假设不是则也把 Hash 值添加到表后(虽然前往的是添加失败)。假设前往失败,则同时前往上一次呈现的位置。
在添加 Hash 值到表中之后,假设添加失败,则判别添加的是一个援用照旧一个对象,假设是援用,则前往 R 标示,假设是对象,则前往 r 标示。由于失败时,会同时前往上一次呈现的位置,因而,R 和 r 标示后面的数字,就是这个位置。
5.3.对象援用的反序列化
PHP 在反序列化处理对象援用时很有意思,假设反序列化的字符串不是 PHP 的 serialize() 自身生成的,而是人为构造或许用其它言语生成的,即便对象援用指向的不是一个对象,它也可以正确地依照对象援用所指向的数据执行反序列化。比如:
echo "<pre class="brush:php;toolbar:false">"; class StrClass { var $a; var $b; } $a = unserialize('O:8:"StrClass":2:{s:1:"a";s:5:"Hello";s:1:"b";r:2;}'); var_dump($a); echo "";
运转结果:
object(StrClass)#1 (2) {
["a"]=>
string(5) "Hello"
["b"]=>
string(5) "Hello"
}
群众会觉察,上面的例子反序列化后,$a->b 的值与 $a->a 的值是一样的,虽然 $a->a
不是一个对象,而是一个字符串。因而假设群众用其它言语来完成序列化的话,不用需非要把 string
作为标量类型来处理,即便依照对象援用来序列化拥有类似字符串内容的复合类型,用 PHP
一样可以正确的反序列化。这样可以更节省序列化后的内容所占用的空间。
6.自定义对象序列化
6.1.PHP 4 中自定义对象序列化
PHP 4 中提供了 __sleep 和 __wakeup 这两个方法来自定义对象的序列化。不过这两个函数并不改动对象序列化的格式,影响的仅仅是被序列化字段的个数。关于它们的简介,在 PHP 手册中写的还算比拟细致。这里就不再多做简介了。
6.2.PHP 5 中自定义对象序列化
PHP 5 中添加了接口(interface)功用。PHP 5 自身提供了一个 Serializable
接口,假设用户在自己定义的类中完成了这个接口,那么在该类的对象序列化时,就会被依照用户完成的方式去执行序列化,并且序列化后的标示不再是
O,而改为 C。C 标示的格式如下:
C:
其中
是完全的用户自己定义的格式,与 PHP 序列化格式可以完全没关,这局部数据由用户自己完成的序列化和反序列化接口方法来维护。
Serializable 接口中定义了 2 个方法,serialize() 和 unserialize($data),这
两个方法不会被直接调用,而是在调用 PHP 序列化函数时,被自动调用。其中 serialize 函数没有参数,它的前往值就是
的内容。而 unserialize($data) 有一个参数 $data,这个参数的值就是
的内容。这样群众应该就明白了,实践上接口中 serialize 方法就是让用户来自己序列化对象中的内容,序列化后的内容格式,PHP
并不关怀,PHP 只担任把它充填到 中,等到反序列化时,PHP 只担任取出这局部内容,然后传给用户完成的
unserialize($data) 接口方法,让用户自己去反序列化这局部内容。
下面举个容易的例子,来标明 Serializable 接口的运用:
class MyClass implements Serializable { public $member; function MyClass() { $this->member = 'member value'; } public function serialize() { return wddx_serialize_value($this->member); } public function unserialize($data) { $this->member = wddx_deserialize($data); } } $a = new MyClass(); echo serialize($a); echo "\n"; print_r(unserialize(serialize($a)));
因而假设想用其它言语来完成 PHP 序列化中的 C 标示的话,也须要提供一种这样的机制,让用户自定义类时,可以自己在反序列化时处理 内容,否则,这些内容就无法被反序列化了。
7.Unicode 字符串的序列化
好了,开头再谈谈 PHP 6 中关于 Unicode 字符串序列化的疑问吧。
说真话,我不如何喜好把字符串搞成双字节 Unicode 这种编码的东西。javascript中也是用这样的字符串,因而在处理字节流的东西时,反而十分的不简约。C#
虽然也是用这种方式来编码字符串,不过还好的是,它提供了周到的编码转换机制,并且提供这种字符串到字节流(实践上是到字节数组)的转换,所以处理起来还
算是可以。但是关于不熟识这个的人来说,转来转去就是个费事。
PHP 6 之前不时是按字节来编码字符串的,到了 PHP 6 突然冒出个 Unicode 编码的字符串来,虽然是可选的,但仍然让人觉得十分不温馨,假设配置不当,老的顺序兼容性都成疑问。
当然加了这个东西现在,许多老的与字符串相关的函数都执行了修正。序列化函数也不例外。因而,PHP 6 中添加了专门的 Unicode 字符串序列化标示 U。PHP 6 中对 Unicode 字符串的序列化格式如下:
U:
这里
但是尚有一点要留意,
的长度,但是也不是只它的字节数,当然也不完全是指它的字符数,确切的说是之它的字符单位数。由于 Unicode String 中采用的是
UTF16 编码,这种编码方式运用 16 位来表示一个字符的,但是并不是一切的都是可以用 16 位表示的,因而有些字符须要两个 16
位来表示一个字符。因而,在 UTF16 编码中,16
位字符算作一个字符单位,一个实践的字符能够就是一个字符单位,也有能够由两个字符单位组成。因而, Unicode String
中字符数并不总是等于字符单位数,而这里的
那
\),依照单个字节写入,关于大于 128 的字符和 \ 字符,则转化为 16 进制编码的字符串,以 \
作为开头,后面四个字节区分是这个字符单位的 16 进制编码,顺序依照由高位到低位陈列,也就是第 16-13
位所对应的16进制数字字符(abcdef 这多个字母是小写)作为第一个字节,第 12-9 位作为第二个字节,第 8-5
位作为第三个字节,开头的第 4-1 位作为第四个字节。顺次编码下来,得到的就是
我以为关于其他言语来说,没有必要完成这种序列化方式,由于用这种方式序列化的内容,关于现在的主流 PHP 服务器来说都是不支持的,不过倒是可以完成它的反序列化,这样未来即便跟 PHP 6 执行数据交流,也可以够相互读懂了。

PHP는 현대 웹 개발, 특히 컨텐츠 관리 및 전자 상거래 플랫폼에서 중요합니다. 1) PHP는 Laravel 및 Symfony와 같은 풍부한 생태계와 강력한 프레임 워크 지원을 가지고 있습니다. 2) Opcache 및 Nginx를 통해 성능 최적화를 달성 할 수 있습니다. 3) PHP8.0은 성능을 향상시키기 위해 JIT 컴파일러를 소개합니다. 4) 클라우드 네이티브 애플리케이션은 Docker 및 Kubernetes를 통해 배포되어 유연성과 확장 성을 향상시킵니다.

PHP는 특히 빠른 개발 및 동적 컨텐츠를 처리하는 데 웹 개발에 적합하지만 데이터 과학 및 엔터프라이즈 수준의 애플리케이션에는 적합하지 않습니다. Python과 비교할 때 PHP는 웹 개발에 더 많은 장점이 있지만 데이터 과학 분야에서는 Python만큼 좋지 않습니다. Java와 비교할 때 PHP는 엔터프라이즈 레벨 애플리케이션에서 더 나빠지지만 웹 개발에서는 더 유연합니다. JavaScript와 비교할 때 PHP는 백엔드 개발에서 더 간결하지만 프론트 엔드 개발에서는 JavaScript만큼 좋지 않습니다.

PHP와 Python은 각각 고유 한 장점이 있으며 다양한 시나리오에 적합합니다. 1.PHP는 웹 개발에 적합하며 내장 웹 서버 및 풍부한 기능 라이브러리를 제공합니다. 2. Python은 간결한 구문과 강력한 표준 라이브러리가있는 데이터 과학 및 기계 학습에 적합합니다. 선택할 때 프로젝트 요구 사항에 따라 결정해야합니다.

PHP는 서버 측에서 널리 사용되는 스크립팅 언어이며 특히 웹 개발에 적합합니다. 1.PHP는 HTML을 포함하고 HTTP 요청 및 응답을 처리 할 수 있으며 다양한 데이터베이스를 지원할 수 있습니다. 2.PHP는 강력한 커뮤니티 지원 및 오픈 소스 리소스를 통해 동적 웹 컨텐츠, 프로세스 양식 데이터, 액세스 데이터베이스 등을 생성하는 데 사용됩니다. 3. PHP는 해석 된 언어이며, 실행 프로세스에는 어휘 분석, 문법 분석, 편집 및 실행이 포함됩니다. 4. PHP는 사용자 등록 시스템과 같은 고급 응용 프로그램을 위해 MySQL과 결합 할 수 있습니다. 5. PHP를 디버깅 할 때 error_reporting () 및 var_dump ()와 같은 함수를 사용할 수 있습니다. 6. 캐싱 메커니즘을 사용하여 PHP 코드를 최적화하고 데이터베이스 쿼리를 최적화하며 내장 기능을 사용하십시오. 7

PHP가 많은 웹 사이트에서 선호되는 기술 스택 인 이유에는 사용 편의성, 강력한 커뮤니티 지원 및 광범위한 사용이 포함됩니다. 1) 배우고 사용하기 쉽고 초보자에게 적합합니다. 2) 거대한 개발자 커뮤니티와 풍부한 자원이 있습니다. 3) WordPress, Drupal 및 기타 플랫폼에서 널리 사용됩니다. 4) 웹 서버와 밀접하게 통합하여 개발 배포를 단순화합니다.

PHP는 현대적인 프로그래밍, 특히 웹 개발 분야에서 강력하고 널리 사용되는 도구로 남아 있습니다. 1) PHP는 사용하기 쉽고 데이터베이스와 완벽하게 통합되며 많은 개발자에게 가장 먼저 선택됩니다. 2) 동적 컨텐츠 생성 및 객체 지향 프로그래밍을 지원하여 웹 사이트를 신속하게 작성하고 유지 관리하는 데 적합합니다. 3) 데이터베이스 쿼리를 캐싱하고 최적화함으로써 PHP의 성능을 향상시킬 수 있으며, 광범위한 커뮤니티와 풍부한 생태계는 오늘날의 기술 스택에 여전히 중요합니다.

PHP에서는 약한 참조가 약한 회의 클래스를 통해 구현되며 쓰레기 수집가가 물체를 되 찾는 것을 방해하지 않습니다. 약한 참조는 캐싱 시스템 및 이벤트 리스너와 같은 시나리오에 적합합니다. 물체의 생존을 보장 할 수 없으며 쓰레기 수집이 지연 될 수 있음에 주목해야합니다.

\ _ \ _ 호출 메소드를 사용하면 객체를 함수처럼 호출 할 수 있습니다. 1. 객체를 호출 할 수 있도록 메소드를 호출하는 \ _ \ _ 정의하십시오. 2. $ obj (...) 구문을 사용할 때 PHP는 \ _ \ _ invoke 메소드를 실행합니다. 3. 로깅 및 계산기, 코드 유연성 및 가독성 향상과 같은 시나리오에 적합합니다.


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

Atom Editor Mac 버전 다운로드
가장 인기 있는 오픈 소스 편집기

Eclipse용 SAP NetWeaver 서버 어댑터
Eclipse를 SAP NetWeaver 애플리케이션 서버와 통합합니다.

PhpStorm 맥 버전
최신(2018.2.1) 전문 PHP 통합 개발 도구

드림위버 CS6
시각적 웹 개발 도구

mPDF
mPDF는 UTF-8로 인코딩된 HTML에서 PDF 파일을 생성할 수 있는 PHP 라이브러리입니다. 원저자인 Ian Back은 자신의 웹 사이트에서 "즉시" PDF 파일을 출력하고 다양한 언어를 처리하기 위해 mPDF를 작성했습니다. HTML2FPDF와 같은 원본 스크립트보다 유니코드 글꼴을 사용할 때 속도가 느리고 더 큰 파일을 생성하지만 CSS 스타일 등을 지원하고 많은 개선 사항이 있습니다. RTL(아랍어, 히브리어), CJK(중국어, 일본어, 한국어)를 포함한 거의 모든 언어를 지원합니다. 중첩된 블록 수준 요소(예: P, DIV)를 지원합니다.
