이 기사는 Laravel 테스트를 위한 PHPUnit 입문 튜토리얼(예제 포함)을 소개합니다. 도움이 필요한 친구들이 참고할 수 있기를 바랍니다.
PHPUnit은 가장 오래되고 유명한 PHP 단위 테스트 패키지 중 하나입니다. 이는 주로 단위 테스트에 사용됩니다. 즉, 가능한 가장 작은 구성 요소로 코드를 테스트할 수 있지만 매우 유연하며 단순한 단위 테스트 이상의 용도로 사용할 수 있습니다.
PHPUnit에는 코드를 쉽게 테스트할 수 있는 간단하고 유연한 어설션이 많이 포함되어 있으며 이러한 어설션은 특정 구성 요소를 테스트할 때 매우 효과적입니다. 그러나 이는 컨트롤러 및 양식 제출 유효성 검사와 같은 고급 코드를 테스트하는 것이 훨씬 더 복잡할 수 있음을 의미합니다.
개발자의 개발을 더 쉽게 만들기 위해 Laravel 프레임워크에는 매우 간단한 PHPUnit 테스트를 작성하여 애플리케이션의 복잡한 부분을 테스트할 수 있는 일련의 애플리케이션 테스트 도우미가 포함되어 있습니다.
이 튜토리얼의 목적은 기본 PHPUnit 어설션과 Laravel 테스트 도우미를 사용하여 PHPUnit 테스트의 기본 사항을 소개하는 것입니다. 이 튜토리얼이 끝나면 애플리케이션에 대한 기본 테스트를 자신있게 작성할 수 있다는 아이디어입니다.
전제 조건
이 튜토리얼에서는 여러분이 이미 Laravel에 익숙하고 애플리케이션 디렉터리에서 명령(예: php artisan 명령)을 실행하는 방법을 알고 있다고 가정합니다. 다양한 테스트 도구의 작동 방식을 알아보기 위해 몇 가지 기본 예제 클래스를 만들 것이므로 이 튜토리얼을 위해 새 애플리케이션을 만드는 것이 좋습니다.
Laravel이 이미 설치되어 있는 경우 다음 명령을 실행하여 새 테스트 애플리케이션을 생성할 수 있습니다:
laravel new phpunit-tests
또는 Composer를 사용하여 직접 새 애플리케이션을 생성할 수 있습니다.
composer create-project laravel/laravel --prefer-dist
다른 설치 방법은 Laravel에서도 찾을 수 있습니다. 선적 서류 비치.
새 테스트 만들기
PHPUnit 사용의 첫 번째 단계는 새 테스트 클래스를 만드는 것입니다. 테스트 클래스의 규칙은 애플리케이션 디렉터리의 ./tests/
아래에 저장된다는 것입니다. 이 폴더에서 각 테스트 클래스의 이름은 <name>Test.php
입니다. 이 형식을 사용하면 PHPUnit은 각 테스트 클래스를 찾을 수 있습니다. --- Test.php
로 끝나지 않는 모든 파일은 무시됩니다. ./tests/
下。在这个文件夹中,每个测试类都被命名为 <name>Test.php
。这种格式允许 PHPUnit 查找每个测试类---它将忽略任何不以 Test.php
结尾的文件。
在新的 Laravel 应用程序中,你会注意到 ./tests/
目录中有两个文件: ExampleTest.php
和 TestCase.php
. TestCase.php
文件是一个引导文件用于在我们的测试中设置 Laravel 环境。这允许我们在测试中使用 Laravel Facades 并为测试助手提供框架,我们将在稍后介绍。 ExampleTest.php
是一个示例测试类,其中包含使用应用程序测试助手的基本测试用例-暂时忽略它。
要创建一个新的测试类,我们可以手动创建一个新文件,或者运行由 Laravel 提供的 Artisan 命令 make:test
为了创建一个名为 BasicTest
的测试类,我们只需要运行这个 artisan 命令:
php artisan make:test BasicTest
Laravel 将创建一个如下所示的基本测试类:
<?php class BasicTest extends TestCase { /** * 一个基本的测试示例。 * * @return void */ public function testExample() { $this->assertTrue(true); } }
这里要注意的最重要的事情是 test
方法名称上的前缀,与 Test
类名后缀一样,这样 test
前缀告诉 PHPUnit 在测试时运行哪些方法。如果您忘记了 test
前缀,那么 PHPUnit 将忽略该方法。
在我们第一次运行测试套件之前,有必要指出 Laravel 提供的默认 phpunit.xml
文件。 PHPUnit 在运行时会自动在当前目录中查找名为 phpunit.xml
或者 phpunit.xml.dist
的文件。您可以在此处配置测试的特定选项。
这个文件中有很多信息,但是现在最重要的部分是在 testsuite
目录定义:
<?xml version="1.0" encoding="UTF-8"?> <phpunit ... > <testsuites> <testsuite name="Application Test Suite"> <directory>./tests/</directory> </testsuite> </testsuites> ... </phpunit>
这将告诉 PHPUnit 运行时在 ./tests/
目录中找到的测试,正如我们之前所知,这是存储测试的约定。
现在我们已经创建了一个基本测试,并且知道了 PHPUnit 配置,现在是第一次运行测试的时候了。
您可以通过运行以下 phpunit
命令来运行测试:
./vendor/bin/phpunit
您应该看到与此类似的输出:
PHPUnit 4.8.19 by Sebastian Bergmann and contributors. .. Time: 103 ms, Memory: 12.75Mb OK (2 tests, 3 assertions)
现在我们已经有了一个有效的 PHPUnit 设置,现在是时候开始编写一个基本测试了。
注意,它会统计2个测试和3个断言,因为 ExampleTest.php
새 Laravel 애플리케이션에는 ./tests/
디렉터리에 ExampleTest.php
및 TestCase.php라는 두 개의 파일이 있다는 것을 알 수 있습니다. <code>TestCase.php
파일은 테스트를 위해 Laravel 환경을 설정하는 데 사용되는 부트스트랩 파일입니다. 이를 통해 우리는 테스트에서 Laravel Facades를 사용할 수 있으며 나중에 다루게 될 테스트 도우미를 위한 프레임워크를 제공합니다. ExampleTest.php
는 Application Test Assistant를 사용하는 기본 테스트 사례가 포함된 예제 테스트 클래스입니다. 지금은 무시하세요.
make:test
Artisan 명령을 실행하면 됩니다. BasicTest 테스트 클래스의 경우 다음 장인 명령을 실행하면 됩니다:
<?php namespace App; class Box { /** * @var array */ protected $items = []; /** * 使用给定项构造框 * * @param array $items */ public function __construct($items = []) { $this->items = $items; } /** * 检查指定的项目是否在框中。 * * @param string $item * @return bool */ public function has($item) { return in_array($item, $this->items); } /** * 从框中移除项,如果框为空,则为 null 。 * * @return string */ public function takeOne() { return array_shift($this->items); } /** * 从包含指定字母开头的框中检索所有项目。 * * @param string $letter * @return array */ public function startsWith($letter) { return array_filter($this->items, function ($item) use ($letter) { return stripos($item, $letter) === 0; }); } }
<?php use App\Box; class BasicTest extends TestCase { public function testHasItemInBox() { $box = new Box(['cat', 'toy', 'torch']); $this->assertTrue($box->has('toy')); $this->assertFalse($box->has('ball')); } }
테스트입니다. code> 메소드 이름 on 접두사는 <code>Test
클래스 이름 접미사와 동일하므로 test
접두사는 테스트 중에 실행할 메소드를 PHPUnit에 알려줍니다. test
접두사를 잊어버린 경우 PHPUnit은 해당 메서드를 무시합니다. 🎜🎜테스트 스위트를 처음 실행하기 전에 Laravel에서 제공하는 기본 phpunit.xml
파일을 지정해야 합니다. PHPUnit은 실행 시 현재 디렉터리에서 phpunit.xml
또는 phpunit.xml.dist
라는 파일을 자동으로 검색합니다. 여기에서 테스트에 대한 특정 옵션을 구성할 수 있습니다. 🎜🎜이 파일에는 많은 정보가 있지만 지금 가장 중요한 부분은 testsuite
디렉터리의 정의입니다. 🎜OK (2 tests, 4 assertions)
에 있음을 알려줍니다. ./tests/
디렉토리 에 있는 테스트는 이전에 알고 있듯이 테스트를 저장하는 규칙입니다. 🎜🎜기본 테스트를 생성하고 PHPUnit 구성을 알았으니 이제 처음으로 테스트를 실행할 차례입니다. 🎜🎜다음 phpunit
명령을 실행하여 테스트를 실행할 수 있습니다. 🎜PHPUnit 4.8.19 by Sebastian Bergmann and contributors. F. Time: 93 ms, Memory: 13.00Mb There was 1 failure: 1) BasicTest::testHasItemInBox Failed asserting that false is true. ./tests/BasicTest.php:12 FAILURES! Tests: 2, Assertions: 4, Failures: 1.
<?php use App\Box; class BasicTest extends TestCase { public function testHasItemInBox() { $box = new Box(['cat', 'toy', 'torch']); $this->assertTrue($box->has('toy')); $this->assertFalse($box->has('ball')); } public function testTakeOneFromTheBox() { $box = new Box(['torch']); $this->assertEquals('torch', $box->takeOne()); // 当前 Box 为空,应当为 Null $this->assertNull($box->takeOne()); } }
ExampleTest.php
파일에는 두 개의 어설션이 있는 테스트가 포함되어 있으므로 2개의 테스트와 3개의 어설션으로 계산됩니다. 우리의 새로운 기본 테스트는 통과한 단일 어설션으로 구성됩니다. 🎜🎜🎜기본 테스트 작성🎜🎜PHPUnit에서 제공하는 기본 어설션을 돕기 위해 먼저 몇 가지 간단한 기능을 제공하는 기본 클래스를 생성하겠습니다🎜在 ./app/
目录中创建一个名为 Box.php
的新文件,并复制此示例类:
<?php namespace App; class Box { /** * @var array */ protected $items = []; /** * 使用给定项构造框 * * @param array $items */ public function __construct($items = []) { $this->items = $items; } /** * 检查指定的项目是否在框中。 * * @param string $item * @return bool */ public function has($item) { return in_array($item, $this->items); } /** * 从框中移除项,如果框为空,则为 null 。 * * @return string */ public function takeOne() { return array_shift($this->items); } /** * 从包含指定字母开头的框中检索所有项目。 * * @param string $letter * @return array */ public function startsWith($letter) { return array_filter($this->items, function ($item) use ($letter) { return stripos($item, $letter) === 0; }); } }
接下来, 打开你的 ./tests/BasicTest.php
类(我们之前创建的类),并删除默认创建的 testExample
方法, 你应该留一个空类。
我们现在将使用七个基本的 PHPUnit 断言来为我们的 Box
类编写测试。这些断言是:
assertTrue()
assertFalse()
assertEquals()
assertNull()
assertContains()
assertCount()
assertEmpty()
assertTrue()
和 assertFalse()
允许你声明一个值等于 true 或 false 。这意味着它们非常适合测试返回布尔值的方法。在我们的 Box
类中,我们有一个名为 has($item)
的方法,当指定的项在 box 中或不在 box 中时,该方法返回对应返回 true 或 false .
要在 PHPUnit 中为此编写测试,我们可以执行以下操作:
<?php use App\Box; class BasicTest extends TestCase { public function testHasItemInBox() { $box = new Box(['cat', 'toy', 'torch']); $this->assertTrue($box->has('toy')); $this->assertFalse($box->has('ball')); } }
注意我们如何只将一个参数传递给 assertTrue()
和 assertFalse()
方法,并且它是 has($item)
方法的输入.
如果您现在运行 ./vendor/bin/phpunit
命令,您会注意到输出包括:
OK (2 tests, 4 assertions)
这意味着我们的测试已经通过。
如果您将 assertFalse()
替换成 assertTrue()
并运行 phpunit
命令,输出将如下所示:
PHPUnit 4.8.19 by Sebastian Bergmann and contributors. F. Time: 93 ms, Memory: 13.00Mb There was 1 failure: 1) BasicTest::testHasItemInBox Failed asserting that false is true. ./tests/BasicTest.php:12 FAILURES! Tests: 2, Assertions: 4, Failures: 1.
这告诉我们第12行的断言未能断言 false
值是 true
- 因为我们将 assertFalse()
替换为 assertTrue()
。
将其交换回来,然后重新运行 PHPUnit 。测试应该再次通过,因为我们已经修复了破损的测试。
接下来,让我们看看 assertEquals()
, 以及 assertNull()
。
assertEquals()
用于比较变量实际值与预期值是否相等。我们用它来检查 takeOne()
方法的返回值是否为 Box 内的当前值。当 Box 为空时,takeOne()
将返回 null
,我们亦可使用 assertNull()
来进行检查。
与 assertTrue()
、assertFalse()
以及 assertNull()
不同,assertEquals()
需要两个参数。第一个参数为 预期 值,第二个参数则为 实际 值。
可参照如下代码实现以上断言(assertions
):
<?php use App\Box; class BasicTest extends TestCase { public function testHasItemInBox() { $box = new Box(['cat', 'toy', 'torch']); $this->assertTrue($box->has('toy')); $this->assertFalse($box->has('ball')); } public function testTakeOneFromTheBox() { $box = new Box(['torch']); $this->assertEquals('torch', $box->takeOne()); // 当前 Box 为空,应当为 Null $this->assertNull($box->takeOne()); } }
运行 phpunit
命令,你应当看到如下输出:
OK (3 tests, 6 assertions)
终于,我们有三个作用于数组有关的断言,我们能够使用它们去检查 Box
类中的 startsWith($item)
方法。 assertContains()
断言传递进来的数组中包含指定值, assertCount()
断言数组的项数为指定数量,assertEmpty()
断言传递进来的数组为空。
让我们来执行以下测试:
<?php use App\Box; class BasicTest extends TestCase { public function testHasItemInBox() { $box = new Box(['cat', 'toy', 'torch']); $this->assertTrue($box->has('toy')); $this->assertFalse($box->has('ball')); } public function testTakeOneFromTheBox() { $box = new Box(['torch']); $this->assertEquals('torch', $box->takeOne()); // Null,现在这个 box 是空的。 $this->assertNull($box->takeOne()); } public function testStartsWithALetter() { $box = new Box(['toy', 'torch', 'ball', 'cat', 'tissue']); $results = $box->startsWith('t'); $this->assertCount(3, $results); $this->assertContains('toy', $results); $this->assertContains('torch', $results); $this->assertContains('tissue', $results); // 如果传递复数断言数组为空 $this->assertEmpty($box->startsWith('s')); } }
保存并再一次运行你的测试:
OK (4 tests, 9 assertions)
恭喜你,你刚刚使用七个基础的 PHPUnit 断言完成了对 Box
类的全部测试。通过这些简单的断言你能够做许多事,对于其他断言,大多数要更复杂,不过它们仍遵循以上使用规则。
在你的程序里,对每个组件进行单元测试在很多情况下都是有必要的,而且也应该成为你开发过程中必不可少的一部分,但这并不是你需要做的全部的测试。当你构建一个包含复杂视图、导航和表单的程序时,你同样想测试这些组件。这时,Laravel的测试助手可以使这些测试像单元测试简单组件一样容易。
我们之前查看在 ./tests/
目录下的默认文件时跳过了 ./tests/ExampleTest.php
文件。 现在打开它,内容如下所示:
<?php class ExampleTest extends TestCase { /** * 一个基本功能测试示例。 * * @return void */ public function testBasicExample() { $this->visit('/') ->see('Laravel 5'); } }
我们可以看到这个测试示例非常简单。在不知道测试助手如何运作的情况下,我们可以猜测它的意思如下:
/
(根目录)如果你打开你的web浏览器,访问我们的程序(如果你没有启动你的web服务器,你可以运行 php artisan serve
),你应该可以在web根目录上看到屏幕上有“Laravel 5”的文本。 鉴于这个测试已经通过了PHPUnit,我们可以很确定地说我们对这个测试示例改造是正确的。
这个测试确保了访问/路径,网页可以返回“'Laravel 5”的文本。一个如此简单的检查也许不代表什么,但如果你的网站上要显示关键信息,它就可以在一个别处的改动导致这个页面无法正常显示正确的信息时,防止你部署一个被损坏的程序。
现在尝试编写自己的测试,更进一步理解它吧。
首先,编辑 ./app/Http/routes.php
,增加一个新的路由。为了教程目的,我们创建希腊字母定义的路由:
<?php Route::get('/',function () { return view('welcome'); }); Route::get('/alpha',function () { return view('alpha'); });
然后,创建视图文件 ./resources/views/alpha.blade.php
,使用 Alpha 作为关键字,保存基本的HTML文件:
<!DOCTYPE html> <html> <head> <title>Alpha</title> </head> <body> <p>This is the Alpha page.</p> </body> </html>
打开浏览器,输入网址: http://localhost:8000/beta
,页面会显示出 "This is the Alpha page." 的内容。
现在我们有了测试用到的模版文件,下一步,我们通过运行命令 make:test
来创建一个新的测试文件:
php artisan make:test AlphaTest
然后变成刚创建好的测试文件,按照框架提供的例子,测试 "alpha" 页面上没有包含 "beta" 。 我们可以使用方法 dontSee()
,它是 see()
的对应的反向方法。
下面代码是上面实现的简单例子:
<?php class AlphaTest extends TestCase { public function testDisplaysAlpha() { $this->visit('/alpha') ->see('Alpha') ->dontSee('Beta'); } }
保存并运行 PHPUnit (./vendor/bin/phpunit
),测试代码应该会全部通过,你会看到像这样的测试状态内容显示:
OK (5 tests,12 assertions)
对于测试来说,测试驱动开发 (TDD) 是非常酷的方法,首先我们先写测试。写完测试并执行它们,你会发现测试没通过,接下来 我们编写满足测试的代码,再次执行测试,使测试通过。 接下来让我们开始。
首先,建立一个 BetaTest
类使用 make:test
artisan 命令:
php artisan make:test BetaTest
接下来,更新测试用例以便检查 /beta
的路由 route 为「Beta」:
<?php class BetaTest extends TestCase { public function testDisplaysBeta() { $this->visit('/beta') ->see('Beta') ->dontSee('Alpha'); } }
现在使用 ./vendor/bin/phpunit
命令来执行测试。结果是一个看起来简洁但不好的错误信息,如下:
> ./vendor/bin/phpunit PHPUnit 4.8.19 by Sebastian Bergmann and contributors. ....F. Time: 144 ms, Memory: 14.25Mb There was 1 failure: 1) BetaTest::testDisplaysBeta 一个对 [http://localhost/beta] 的请求失败了。收到状态码 [404]。 ... FAILURES! Tests: 6, Assertions: 13, Failures: 1.
我们现在需要创建这个不存在的路由。让我们开始。
首先,编辑 ./app/Http/routes.php
文件来创建新的 /beta
路由:
<?php Route::get('/', function () { return view('welcome'); }); Route::get('/alpha', function () { return view('alpha'); }); Route::get('/beta', function () { return view('beta'); });
接下来,在 ./resources/views/beta.blade.php
下创建如下视图模版:
<!DOCTYPE html> <html> <head> <title>Beta</title> </head> <body> <p>This is the Beta page.</p> </body> </html>
现在再一次执行 PHPUnit,结果应该再一次回到绿色。
> ./vendor/bin/phpunit PHPUnit 4.8.19 by Sebastian Bergmann and contributors. ...... Time: 142 ms, Memory: 14.00Mb OK (6 tests, 15 assertions)
这样我们就通过在完成新的页面之前写测试的方式,对 测试驱动开发 进行了实践。
Laravel 也提供一个辅助函数 (click()
) 允许测试点击页面中存在的连接 ,以及一个方法 (seePageIs()
) 检查点击展示的结果页面。
让我们使用这两个辅助函数去执行在 Alpha 和 Beta 页面的链接。
首先,我们更新我们的测试。打开 AlphaTest
类,我们将添加一个新的测试方法,这将点击 「alpha」页面上的「Next」链接跳转到 「beta」页面。
新的测试代码如下:
<?php class AlphaTest extends TestCase { public function testDisplaysAlpha() { $this->visit('/alpha') ->see('Alpha') ->dontSee('Beta'); } public function testClickNextForBeta() { $this->visit('/alpha') ->click('Next') ->seePageIs('/beta'); } }
注意到,在我们新建的 testClickNextForBeta()
方法中,我们并没有检查每一个页面的内容。 其他测试都成功的检查了两个页面的内容,所以这里我们只关心点击 「Next」链接将发送到 /beta
。
你现在可以运行测试组件了,但就像预料的一样测试将不通过,因为我们还没有更新我们的 HTML。
接下来,我们将更新 BetaTest
来做类似的事情:
<?php class BetaTest extends TestCase { public function testDisplaysBeta() { $this->visit('/beta') ->see('Beta') ->dontSee('Alpha'); } public function testClickNextForAlpha() { $this->visit('/beta') ->click('Previous') ->seePageIs('/alpha'); } }
接下来,我们更新我们的 HTML 模版。
./resources/views/alpha.blade.php
:
<!DOCTYPE html> <html> <head> <title>Alpha</title> </head> <body> <p>This is the Alpha page.</p> <p><a href="/beta">Next</a></p> </body> </html>
./resources/views/beta.blade.php
:
<!DOCTYPE html> <html> <head> <title>Beta</title> </head> <body> <p>This is the Beta page.</p> <p><a href="/alpha">Previous</a></p> </body> </html>
保存文件,再一次执行 PHPUnit:
> ./vendor/bin/phpunit PHPUnit 4.8.19 by Sebastian Bergmann and contributors. F....F.. Time: 175 ms, Memory: 14.00Mb There were 2 failures: 1) AlphaTest::testDisplaysAlpha Failed asserting that '<!DOCTYPE html> <html> <head> <title>Alpha</title> </head> <body> <p>This is the Alpha page.</p> <p><a href="/beta">Next</a></p> </body> </html> ' does not match PCRE pattern "/Beta/i". 2) BetaTest::testDisplaysBeta Failed asserting that '<!DOCTYPE html> <html> <head> <title>Beta</title> </head> <body> <p>This is the Beta page.</p> <p><a href="/alpha">Previous</a></p> </body> </html> ' does not match PCRE pattern "/Alpha/i". FAILURES! Tests: 8, Assertions: 23, Failures: 2.
然而测试失败了。如果你仔细观察我们的新 HTML,你将注意到我们分别有术语 beta
和 alpha
在 /alpha
和 /beta
页面。这意味着我们需要稍微更改我们的测试让它们与误报不匹配。
在每一个 AlphaTest
和 BetaTest
类,更新 testDisplays*
方法去使用 dontSee('<page> page')
。通过这种方式,这将仅仅匹配字符串而不是那个术语。
两个测试文件如下所示:
./tests/AlphaTest.php
:
<?php class AlphaTest extends TestCase { public function testDisplaysAlpha() { $this->visit('/alpha') ->see('Alpha') ->dontSee('Beta page'); } public function testClickNextForBeta() { $this->visit('/alpha') ->click('Next') ->seePageIs('/beta'); } }
./tests/BetaTest.php
:
<?php class BetaTest extends TestCase { public function testDisplaysBeta() { $this->visit('/beta') ->see('Beta') ->dontSee('Alpha page'); } public function testClickNextForAlpha() { $this->visit('/beta') ->click('Previous') ->seePageIs('/alpha'); } }
再一次运行你的测试,所有的测试都应该通过了。我们现在已经测试我们所有的新文件,包括页面中的 Next/Previous 链接。
通过 Semaphore 对 PHPUnit 持续集成
通过 Semaphore设置 持续集成你可以自动执行你的测试。
这样每一次你进行 git push 提交代码的时候都会执行你的测试,并且 Semaphore 预装了所有最新的 PHP 版本。
如果你还没有一个 Semaphore 账户, 先去 注册一个免费的 Semaphore 账户 。接下来需要做的是将它 添加到你的项目,并按照提示逐步去做来执行你的测试:
composer install --prefer-source phpunit
关于 PHP 持续集成 的更多信息,请参照 Semaphore 文档。
你应该注意到本教程中的所有测试都有一个共同的主题:它们都非常简单。 这是学习如何使用基本的测试断言和辅助函数,并且尽可能的使用它们的好处之一。编写测试越简单,测试就越容易理解和维护。
掌握了本教程中介绍的 PHPUnit 断言之后,你还可以去 PHPUnit 文档 找到更多内容。 所有的断言都遵循基本的模式,但你会发现,在大多数测试中都会返回基本的断言。
对于 PHPUnit 断言来说,Laravel 的测试辅助函数是极好的补充,这让应用程序的测试变的非常容易。也就是说,重要的是要认识到,对于我们写测试,我们只检查关键信息,而不是整个页面。这使得测试变得简单,并允许页面内容随着应用程序的变化而变化。如果关键信息仍然存在,测试仍然通过,每个人都会满意。
위 내용은 Laravel 테스트를 위한 PHPUnit 입문 튜토리얼 소개(예제 포함)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!