- 投稿日:2019-07-30T23:27:05+09:00
Atom利用時にのみ反映される環境変数
経緯
パスを通す事ができないけどPHPは手元にあるって状況でいろいろ探してたところ、Atomの環境変数にPHPを突っ込む対応ができたのでメモ。
他にも同じようなことをしているものはあったけど、ローカルのパスを指定して追加するって対応がなかなか見つからなかった、数日前の自分が見つけられるようになれば良いんだけど。
対応手順
init.coffee を開いて下部の内容を追加するとパスに追加が可能。
init.coffeeの開き方は「file→init script」
※japanese-menuで日本語化している場合、「ファイル→起動スクリプト」で開けます
process.env.PATH = [process.env.PATH,"C:\\xampp\\php",].join(";")
\
(バックスラッシュ)はエスケープされるので2つ繋げて\\
にする。- 追記後はリロードすること。「Ctrl+Shif+P → window: reload」
補足
Atom上でCtrl+Shift+Iで開発ツールを出して、コンソール上にprocess.env.PATHと打ち込むと現状の通っているパスリストが見れる。
上記対応後に打ち込んで、結果が望んでいた通り追加されていれば完了。
- 投稿日:2019-07-30T20:46:32+09:00
計算量O(n)の革命的なソートアルゴリズムをPHPでも
計算量O(n)の画期的なソートアルゴリズムであるスターリンソートをHaskell で実装してみた #Haskell - Qiita
スターリンソートは当然、PHPでも簡単に実装することができます。 https://3v4l.org/g9bR2
<?php namespace stalin; function sort(array $xs): array { if (count($xs) <= 1) { return $xs; } $zs = $xs; $x = array_shift($zs); $y = array_shift($zs); if ($x <= $y) { return [$x, ...sort([$y, ...$zs])]; } return sort([$x, ...$zs]); }動かしてみます。
echo json_encode(\stalin\sort([1, 2, 1, 1, 4, 3, 9])); // [1,2,4,9]完璧ですね。
PHP 7.3? そんな古いバージョンのことは知りません参考
スターリンソートが流行ってるらしいので… https://t.co/5sor37Dl2D
— tadsan.exe (@tadsan) July 30, 2019
- 投稿日:2019-07-30T19:58:50+09:00
「ようこそ・・・『テストの世界』へ・・・」
はじめに
本記事では、テストをまだ書いたことのない人向けに
「今日からテストを書いてみようかな」
と思ってもらえるよう、チュートリアル風に簡単なテストの流れを説明します。
題材はLaravelですが、他のフレームワークでも同じようなことはできると思います。
前提
本記事の対象者
- Laravel初心者で、
- テストをまだ書いたことの無い人
- テストで何ができるのか知らない人
- テストに興味はあるが忙しくてどう書けば良いのかまだ調べられていない人
本記事で取り扱うこと
- ごく簡単なHTTPリクエストのテストの書き方
本記事で取り扱わないこと
- テストの全般的な話(利点、注意点など)
- テストケースの作り方
- CIツールによるテスト自動化
環境
- Laravel 5.8.x
簡単な画面(HTTPレスポンス)のテスト
まずは、Laravelを触ったことのある人なら一度は見たことのある?Welcome画面をテストしてみます。
これが正常に表示されることを、目視ではなくテストコードで確認してみましょう。
Laravelをインストールすると、既に
tests/Feature/ExampleTest.php
という、テストコードのサンプルが存在します。tests/Feature/ExampleTest.php<?php namespace Tests\Feature; use Tests\TestCase; use Illuminate\Foundation\Testing\RefreshDatabase; class ExampleTest extends TestCase { /** * A basic test example. * * @return void */ public function testBasicTest() { $response = $this->get('/'); $response->assertStatus(200); } }この
ExampleTest
は、Tests\TestCase
を継承しており、テストに関する様々なメソッドが使えます。
$response = $this->get('/')
で、'/'
にアクセスした(GETリクエストした)結果のレスポンス1が$response
に代入されます。
そして、$response->assertStatus(200)
で、そのレスポンスが正常であること(ステータスコードが200 OK
であること)をチェックしています。ステータスコードについては以下を参考にしてください。
この
ExampleTest
を実行するには、Laravelのルートフォルダで以下コマンドを実行してください。$ ./vendor/bin/phpunit ./tests/Feature/ExampleTest.phpすると、以下の結果が表示されました。
PHPUnit 7.5.12 by Sebastian Bergmann and contributors. . 1 / 1 (100%) Time: 448 ms, Memory: 14.00 MB OK (1 test, 1 assertion)OK、と表示されています。
つまり、
'/'
へアクセスした(GETリクエストした)結果、正常にレスポンスが返ってきた(HTTPレスポンスステータスコードが200 OK
だった)ということです。これをテストコードで確認することができました。
ビューのテスト
ただ、このテストでわかったのは、正常にレスポンスが返ってきた、ということまでです。
Welcome画面が表示されたのかどうかまでテストできていません。
現状のルーティングを見ると、
'/'
へアクセスすると、'welcome'
というビューが表示されることがわかります。routes/web.phpRoute::get('/', function () { return view('welcome'); });
welcome
というビューは、具体的にはresources/views/welcome.blade.php
というビューのテンプレートです。この
resources/views/welcome.blade.php
こそがあのWelcome画面なので、このテンプレートが使われているかどうかをテストで確認してみます。
tests/Feature/ExampleTest.php
に以下のコードを追加します。tests/Feature/ExampleTest.phppublic function testBasicTest() { $response = $this->get('/'); $response->assertStatus(200) ->assertViewIs('welcome'); // 追加 }このように
assertViewIs
メソッドで、どんなビューが使われたのかをテストすることができます。修正後の
ExampleTest
を実行してみます。$ ./vendor/bin/phpunit ./tests/Feature/ExampleTest.php // 略 OK (1 test, 2 assertions)こちらも結果はOKとなりました。
あのWelcome画面が表示されていることを、テストコードで確認することができました。
さらに
assertSee
メソッドを使うと、表示されている2文字列もテストできます。
Welcome画面にLaravel
の文字が表示されていることを、テストコードで確認してみましょう。
tests/Feature/ExampleTest.php
に以下のコードを追加します。tests/Feature/ExampleTest.phppublic function testBasicTest() { $response = $this->get('/'); $response->assertStatus(200) ->assertViewIs('welcome') ->assertSee('Laravel'); // 追加 }修正後の
ExampleTest
を実行してみます。$ ./vendor/bin/phpunit ./tests/Feature/ExampleTest.php // 略 OK (1 test, 3 assertions)こちらも結果はOKとなりました!
ログイン中かどうかを絡めたテスト
ここから先は、ユーザーがログイン中かそうでないかによって結果が異なる画面をテストしていきます。
Laravelでは以下コマンドでユーザー登録画面やログイン画面等を作成できるので、これを実行します。
$ php artisan make:authこのコマンドの詳細については、以下の記事を参考にしてください。
Welcome画面にユーザー登録画面とログイン画面へのリンクが追加されました。
以下、テストではなく人力でユーザー登録画面にアクセスし、ユーザー登録を行なっています。
ユーザー登録が完了すると、以下のホーム画面へ遷移します。なお、ログイン画面からログインした場合も、このホーム画面へ遷移します。
ホーム画面は、ログイン中の時のみ表示されます。
ログインしていない状態で直接
/home
へアクセスすると、ホーム画面は表示されず、ログイン画面へリダイレクトされます。この「ホーム画面は、ログイン中の時のみ表示される」ということをテストコードで確認してみましょう。
ホーム画面の表示は、
php artisan make:auth
コマンドによって作成された、app\Http\Controlles\HomeController
で行われています。この
HomeController
をテストするためのHomeControllerTest
を作成します。以下コマンドを実行すると、テストの雛形を作成できます。
$ php artisan make:test HomeControllerTest作成されたテストの雛形の内容は、以下になります。
tests/Feature/HomeControllerTest.php<?php namespace Tests\Feature; use Tests\TestCase; use Illuminate\Foundation\Testing\WithFaker; use Illuminate\Foundation\Testing\RefreshDatabase; class HomeControllerTest extends TestCase { /** * A basic feature test example. * * @return void */ public function testExample() { $response = $this->get('/'); $response->assertStatus(200); } }この
tests/Feature/HomeControllerTest.php
を以下の通り編集します。tests/Feature/HomeControllerTest.phppublic function testExample() { $response = $this->get('/home'); // 変更(ホーム画面のパスに変更) $response->assertStatus(200) ->assertViewIs('home') // 追加(ここでの'home'は、ホーム画面で使われているビュー名) ->assertSee('You are logged in!'); // 追加(ホーム画面で表示されているメッセージ) }この
HomeControllerTest
を、以下コマンドで実行します。$ ./vendor/bin/phpunit ./tests/Feature/HomeControllerTest.phpすると、以下の通り、OKではなくFAILURES!となりました。
PHPUnit 7.5.12 by Sebastian Bergmann and contributors. F 1 / 1 (100%) Time: 442 ms, Memory: 14.00 MB There was 1 failure: 1) Tests\Feature\HomeControllerTest::testExample Expected status code 200 but received 302. Failed asserting that false is true. /var/www/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:133 /var/www/tests/Feature/HomeControllerTest.php:20 FAILURES! Tests: 1, Assertions: 1, Failures: 1.
Expected status code 200 but received 302.
と出力されています。ログイン中でない状態でアクセスしたので、リダイレクトを示す
302
のステータスコードが返ってきた、ということです。そこで、ログインした状態を作り出してテストしてみます。
DBには、先ほど人力でユーザー登録画面にアクセスして登録したユーザーデータが存在するので、テストではこのユーザーを使うことにします。
# select id, name from users; id | name ----+-------------------- 1 | shonansurvivors (1 rows)
tests/Feature/HomeControllerTest.php
を以下の通り編集します。tests/Feature/HomeControllerTest.php<?php namespace Tests\Feature; use App\User; // 追加 use Tests\TestCase; use Illuminate\Foundation\Testing\WithFaker; use Illuminate\Foundation\Testing\RefreshDatabase; class HomeControllerTest extends TestCase { public function testExample() { $response = $this ->actingAs(User::find(1)) // 追加 ->get(route('home')); $response->assertStatus(200) ->assertViewIs('home') ->assertSee('You are logged in!'); } }
actingAs
メソッドで、そのユーザーとしてログイン済の状態になります。また、
User::find(1)
で、DBのusersテーブルからid
が1
であるユーザーを取ってきています。改めて
HomeControllerTest
を、以下コマンドで実行します。$ ./vendor/bin/phpunit ./tests/Feature/ExampleTest.phpすると、OKとなりました。
OK (1 test, 3 assertions)ログイン中の状態で
/home
へアクセスすると、
- ステータスコードが
200
となることhome
というビューが使われていることYou are logged in!
が、html中に存在することが確認できました。
このようにログイン中かどうかが絡む機能も、テストコードで確認できます。
テストに必要なDBのデータを自動で準備する
今回はたまたま開発環境のDBにユーザーのデータが存在し、テストではこれを利用してログインを行いましたが、 テストに必要なDBのデータ準備もコードで自動化してみます。
Laravelではテスト用のDBのデータを作るのに、ファクトリというものを使う方法があります。
database/factories/UserFactory.php
が、ユーザーデータを作るためのファクトリです。database/factories/UserFactory.php<?php use App\User; use Illuminate\Support\Str; use Faker\Generator as Faker; $factory->define(User::class, function (Faker $faker) { return [ 'name' => $faker->name, 'email' => $faker->unique()->safeEmail, 'email_verified_at' => now(), 'password' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', // password 'remember_token' => Str::random(10), ]; });このファクトリの詳細については本記事では触れませんが、これを使うといい感じにダミーのデータを作ってくれます。
ファクトリについてもっと知りたい方は、以下記事を参考にしてください。
このファクトリを使うよう、
tests/Feature/HomeControllerTest.php
を以下の通り変更します。tests/Feature/HomeControllerTest.php<?php namespace Tests\Feature; use App\User; // 追加 use Tests\TestCase; use Illuminate\Foundation\Testing\WithFaker; use Illuminate\Foundation\Testing\RefreshDatabase; class HomeControllerTest extends TestCase { public function testExample() { $user = factory(User::class)->create(); // 変更(ファクトリでユーザーデータを作成) $response = $this ->actingAs($user) // 変更(ファクトリで作ったユーザーデータでログイン中状態を作る) ->get(route('home')); $response->assertStatus(200) ->assertViewIs('home') ->assertSee('You are logged in!'); } }ファクトリで作成されたユーザーにて、ログイン中の状態を作っています。
以下コマンドで
HomeControllerTest
を実行すると、結果はOKとなりました。$ ./vendor/bin/phpunit ./tests/Feature/HomeControllerTest.php // 略 OK (1 test, 3 assertions)ただ、これだと
HomeControllerTest
実行のたびにDBにダミーのユーザーデータが1件また1件...と作られてしまいます。sample=# select id, name from users; id | name ----+-------------------- 1 | shonansurvivors 2 | Dr. Jayce Wiegand 3 | Miss Shayna Deckow (3 rows)2件目と3件目がファクトリで作られたダミーのユーザーデータです。
これに対しては、ひとつのやり方として、テスト用の
DatabaseTransactions
を使うという方法があります。これを使うと、テストの実行後にDBをロールバックし、テスト実行中にDBに作ったデータが残らなくなります。
tests/Feature/HomeControllerTest.php
で、DatabaseTransactions
を使うようにします。tests/Feature/HomeControllerTest.php<?php namespace Tests\Feature; use App\User; use Tests\TestCase; use Illuminate\Foundation\Testing\DatabaseTransactions; // 追加 use Illuminate\Foundation\Testing\WithFaker; use Illuminate\Foundation\Testing\RefreshDatabase; class HomeControllerTest extends TestCase { use DatabaseTransactions; // 追加 public function testExample() { // 以下変更点なしにつき省略以下コマンドで
HomeControllerTest
を実行すると、結果は引き続きOKとなりますが・・・$ ./vendor/bin/phpunit ./tests/Feature/HomeControllerTest.php // 略 OK (1 test, 3 assertions)テスト終了後のDBを見ると、ユーザーデータはテスト開始前時点から増えてはいません(3件のまま)でした。
sample=# select id, name from users; id | name ----+-------------------- 1 | shonansurvivors 2 | Dr. Jayce Wiegand 3 | Miss Shayna Deckow (3 rows)これでDBにダミーデータが残ることなく、何度でもテストを実行できます。
最後に
以上で、本記事のテスト体験ツアーは終了です。
テストでどのようなことができるか、イメージはつかめたでしょうか。
本記事をきっかけに、テストを書き始めていただければ幸いです。
「納期は守る」「品質も確保する」
「両方」やらなくちゃあならないってのが「エンジニア」のつらいところだな参考
- 投稿日:2019-07-30T19:31:06+09:00
AWS上でphpをrpmでインストールする時のrequireメッセージに注意
Error: Package: php-mysql-5.3.3-49.el6.x86_64 (/php-mysql-5.3.3-49.el6.x86_64) Requires: php-common(x86-64) = 5.3.3-49.el6 Removing: php-common-5.3.3-49.el6.x86_64 (installed)とか言ってるくせに、本当にいるのはphp-pdoと言うこの詐欺メッセージ。だって、既にこのcommonは入れてるから。
php-pdoとは
php-data-objectのことで、データベースの文字列をphpの扱うオブジェクトデータに変換するものです。これを引っ張ってきてください。
- 投稿日:2019-07-30T19:00:49+09:00
Docker学習メモ
Docker学習メモ
>>> 編集中
はじめに
自分でアプリケーションを開発していく上で、開発環境のWindowsと本番環境のLinuxで差分が出てしまうのを
解決したかったので、Dockerを実際に使ってみましたそれに伴ってDockerの概要や使ってみた感想などをまとめてみました
Dockerについて
まず、Dockerとは仮想環境を提供するソフトウェアの事を指します
コンテナと呼ばれるアプリケーションを依存対象と共にカプセル化することが出来ますコンテナは隔離されたOSを持ち、その上でアプリケーションを稼働させることが出来ます
またホストOSとリソースを共有出来ることが特徴ですDockerを利用するうえで、イメージとコンテナの概念は重要で、下記リンクに大まかな概要が掲載されています
Dockerの利点
- フレームワークやミドルウェアを自分で構築する必要がない
例えばLaravelを利用した開発を行う場合に、ホストOSにPHPをインストールしなくても
Bitnamiが提供するLaravelのイメージがDocker Hubと呼ばれるリポジトリに配布されているため
自分でPHP、Laravelインストールをしなくても簡単に環境を構築できますまた新しいフレームワークや技術が登場しても、Docker Hubでイメージが配布されていれば、自分で環境構築をする必要はありません
- 複数の開発環境の統一できる
DockerfileにOSやパッケージのバージョンを指定することが出来るため
開発環境ではUbuntu
、RHEL
などのOSやRuby
、Nodejs
、PHP
、Go
などの言語のバージョンを統一できますまた
apt
、pip
などのパッケージマネージャ以外のライブラリをインストールする場合、自分でmsiをダウンロードしたり
install.shなどを作成することもあると思うのですが、Dockerの場合それは必要ありません僕の認識ではseleniumは、ChromeDriverを手でインストールする必要がありますが
コンテナの場合、その作業は必要なく、ホストの\\Program Files
や/opt
、/var
などの汚染を気にすることもありません
- 開発環境と本番環境の差分を埋めることが出来る
アプリケーションやミドルウェアではDocker Engine上で稼働するため
開発環境、本番環境のホストOS上でDocker Engineプロセスさえ起動していれば、アプリケーションは差分なく稼働することが出来ます
- VMと比べて早い
リンクを参照すると早いかもしれません
ゲストOSがない分、コンテナの起動が早くキャッシュを利用したコンテナの再ビルドはとても高速です
vagrant up
に比べて体感速度レベルで起動速度が違いますDockerを理解する
まずDockerのアーキテクチャについて下記のリンクを参照してください
簡単にコンテナでコマンドを実行する流れを紹介します
例えばCent OSコンテナでcat /etc/system-release
コマンドを実行する場合は下記の手順を踏むことで行えます
docker pull centos
コマンドで、コンテナのひな形となるイメージファイルを取得docker run --name mycontainer centos
コマンドで、イメージからコンテナを生成・起動・ログインを行う- コンテナログイン後、
cat /etc/system-release
コマンドでOS情報を取得する // CentOSと表示されるはずexit
コマンドでコンテナから抜ける補足ですが、docker runの引数に指定されたimageがローカル環境に存在しない場合、Docker Hubから取得できるため
上記の作業はワンライナーで行えます
docker run -it centos cat /etc/system-release
上記の他にも、コンテナの停止、削除、コンテナのイメージ化などがあります
詳細などは下記のdockerチュートリアルなどを参照してください
Dockerコンテナの作成、起動〜停止まで // 再度掲載
サブコマンドリファレンスDocker + Laravel
Docker + Nginx + MySQL
- 投稿日:2019-07-30T18:52:26+09:00
PHP で 配列の値を順番までランダムにして取得する関数
PHP で普通に配列から値を取得しようとすると
array_rand()
で取得したキーを元の配列に入れる微妙な面倒さがあり、ついでにこれだけだと順番がランダムになっていなくて、それもまた微妙に面倒。ということで配列と取り出す数を指定してサクッとランダムに取り出せる関数を書いた
function array_rand_and_shuffle(array $array, int $num_req) { shuffle($array); // 配列の要素数が取得数に満たない場合はシャッフルだけする if(count($array) <= $num_req) return $array; $random_keys = array_rand($array, $num_req); $rand_array = []; foreach($random_keys as $key){ $rand_array[] = $array[$key]; } return $rand_array; }
- 投稿日:2019-07-30T18:22:53+09:00
ibm cloudにdeployしていたappが動かなくなったので対応した
いつの間にか、ibm cloudで動かしてたappが動かなくなってたので、その時やったこと。
A service instance was not found in this resource group and region us-south
このエラーは正直良くわからないけど、なんか適当にツールを追加したらいつの間にか、過去ツールが動くようになっていて、追加したツールを削除した。しばらくログインしてなかったからだと思う。
ibm cloudは、Web UIがコロコロ変更されるので、正直、よくわからん状態。
Pipeline | Delivery Pipeline
でDeploy Stage
の構成をpipline img(latest:2.3)にするIMPORTANT: The Continuous Delivery Pipeline is being updated to pull a specific Docker image to run each job, either one of the built-in ones or your own custom image. This job is being run with the oldest image. You should consider moving to a more recent one, to get more recent versions of the included tools.
バージョンが古かったらしいので、最新にしてみた。
- memory不足を言われるので、manifest.ymlで指定して
cf push
するError restarting application: Server error, status code: 400, error code: 100005, message: You have exceeded your organization's memory limit: app requested more memory than available
WebのGUIからの操作で何故かメモリが足りないエラーが出て、デプロイできない。CUIからmanifest.ymlでmemory:256MBを指定して、pushしたらデプロイ成功した。
- 投稿日:2019-07-30T18:22:53+09:00
ibm cloudにdeployしていたappが動かなくなっていたので対応した
いつの間にか、ibm cloudで動かしてたappが動かなくなってたので、その時やったこと。
A service instance was not found in this resource group and region us-south
このエラーは正直良くわからないけど、なんか適当にツールを追加したらいつの間にか、過去ツールが動くようになっていて、追加したツールを削除した。しばらくログインしてなかったからだと思う。
ibm cloudは、Web UIがコロコロ変更されるので、正直、よくわからん状態。
Pipeline | Delivery Pipeline
でDeploy Stage
の構成をpipline img(latest:2.3)にするIMPORTANT: The Continuous Delivery Pipeline is being updated to pull a specific Docker image to run each job, either one of the built-in ones or your own custom image. This job is being run with the oldest image. You should consider moving to a more recent one, to get more recent versions of the included tools.
バージョンが古かったらしいので、最新にしてみた。
- memory不足を言われるので、manifest.ymlで指定して
cf push
するError restarting application: Server error, status code: 400, error code: 100005, message: You have exceeded your organization's memory limit: app requested more memory than available
WebのGUIからの操作で何故かメモリが足りないエラーが出て、デプロイできない。CUIからmanifest.ymlでmemory:256MBを指定して、pushしたらデプロイ成功した。
- 投稿日:2019-07-30T17:24:13+09:00
【PHPUnit】phpunit.xmlを使ってグローバル変数を設定したい
PHPUnitを使ってテストコードを書くとき、
phpunit.xml を使って\$_POST, \$_GET, $_ENV (その他いろいろ)などが設定できる。ディレクトリ構成App/ src/ Environment.php tests/ EnvironmentTest.php vendor/ composer.json phpunit.xmlphpunit.xml<phpunit> <php> <env name="ENVIRONMENT" value="localhost" force="true"/> </php> </phpunit>Environment.php<?php class Environment { public static function getEnvironment() { return \getenv('ENVIRONMENT'); } }EnvironmentTest.php<?php require_once __DIR__.'/../src/Environment.php'; use PHPUnit\Framework\TestCase; class EnvironmentTest extends TestCase { /** * @test */ public function xmlで環境変数が設定できている(): void { $this->assertSame('localhost', Environment::getEnvironment()); } }phpunit.xmlはカレントディレクトリにないとデフォルトでは読み込まれない
カレントディレクトリにphpunit.xmlがある場合
C:\App> vendor/bin/phpunit tests\EnvironmentTest.php PHPUnit 8.0.6 by Sebastian Bergmann and contributors. . 1 / 1 (100%) Time: 813 ms, Memory: 4.00 MB OK (1 test, 1 assertion)testsディレクトリに移動して実行
C:\App> cd tests C:\App\tests> ../vendor/bin/phpunit EnvironmentTest.php PHPUnit 8.0.6 by Sebastian Bergmann and contributors. F 1 / 1 (100%) Time: 1.66 seconds, Memory: 4.00 MB There was 1 failure: 1) EnvironmentTest::xmlで環境変数が設定できている Failed asserting that false is identical to 'localhost'. C:\App\tests\EnvironmentTest.php:13カレントディレクトリにないXML設定ファイルを読み込む
https://phpunit.readthedocs.io/ja/latest/textui.html#textui-clioptions
--configuration, -c
設定を XML ファイルから読み込みます。 詳細は XML 設定ファイル を参照ください。
phpunit.xml あるいは phpunit.xml.dist (この順番で使用します) が現在の作業ディレクト>リに存在しており、かつ --configuration が使われていない場合、設定が自動的にそのファイルから読み込まれます。
指定されているのがディレクトリで、 phpunit.xml あるいは phpunit.xml.dist (この順番で使用します) がそのディレクトリ内に存在する場合、設定が自動的にそのファイルから読み込まれます。
C:\App\tests> ../vendor/bin/phpunit --configuration ../phpunit.xml EnvironmentTest.php PHPUnit 8.0.6 by Sebastian Bergmann and contributors. . 1 / 1 (100%) Time: 796 ms, Memory: 4.00 MB OK (1 test, 1 assertion)XML設定ファイルを読み込まない
https://phpunit.readthedocs.io/ja/latest/textui.html#textui-clioptions
--no-configuration
現在の作業ディレクトリにある phpunit.xml および phpunit.xml.dist を無視します。
C:\App\tests> cd .. C:\App\> vendor/bin/phpunit --no-configuration tests/EnvironmentTest.php PHPUnit 8.0.6 by Sebastian Bergmann and contributors. F 1 / 1 (100%) Time: 597 ms, Memory: 4.00 MB There was 1 failure: 1) EnvironmentTest::xmlで環境変数が設定できている Failed asserting that false is identical to 'localhost'. C:\App\tests\EnvironmentTest.php:13 FAILURES! Tests: 1, Assertions: 1, Failures: 1.
- 投稿日:2019-07-30T17:19:26+09:00
CocでPHP定義元辿ろうとすると PHP Fatal error: Uncaught Error: Class 'MessagePackUnpacker' not found in が起きる。
https://qiita.com/masakuni-ito/items/d7a6c2de47344f0bbd1c の続き。
NeovimでCoc入れて、PHPの定義元移動試してみたら、PHPでエラーが起きたので、それ解決したら違うエラーが起きた。
PHP Fatal error: Uncaught Error: Class 'MessagePackUnpacker' not found in /Users/ito_masakuni/.cache/dein/repos/github.com/lvht/phpcd.vim/vendor/lvht/msgpack-rpc/src/MsgpackMessenger.php:18 Stack trace: #0 /Users/ito_masakuni/.cache/dein/repos/github.com/lvht/phpcd.vim/php/main.php(30): Lvht\MsgpackRpc\MsgpackMessenger->__construct(Object(Lvht\MsgpackRpc\StdIo)) #1 {main} thrown in /Users/ito_masakuni/.cache/dein/repos/github.com/lvht/phpcd.vim/vendor/lvht/msgpack-rpc/src/MsgpackMessenger.php on line 18まぁ……たぶん、msgpack、ないんだな……。
入れる。
$ pecl install msgpack downloading msgpack-2.0.3.tgz ... Starting to download msgpack-2.0.3.tgz (45,769 bytes) .............done: 45,769 bytes 20 source files, building running: phpize Configuring for: PHP Api Version: 20180731 Zend Module Api No: 20180731 Zend Extension Api No: 320180731 ・ ・ ・これでCocの定義元移動ができた。Coc凄まじい。
- 投稿日:2019-07-30T17:09:52+09:00
PHP実行すると dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.62.dylib のエラーが起きる。
NeovimでCoc入れて、PHPの定義元移動試してみたら、下記のようなエラー。
dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.62.dylib Referenced from: /usr/local/bin/php Reason: image not found最初Cocかnodeかで起きてるのかと思ってicu4cをアップデートも考えたんだけど、phpが参照元って書いてあるので、phpを叩いてみると同じエラーが起きる。
$ /usr/local/bin/php dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.62.dylib Referenced from: /usr/local/bin/php Reason: image not found
何だお前、最近使わなかったけど、動いてなかったんだな……。
$ brew upgrade php php 7.2.12 -> 7.3.7 ==> Upgrading php ==> Installing dependencies for php: apr, openssl, argon2, brotli, c-ares, libidn, libmetalink, libssh2, jansson, libev, libevent, nghttp2, openldap, rtmpdump, curl-openssl, freetds, libpng, freetype, pcre, readline, sqlite, python, glib, libpq, libsodium, libzip, libtiff and webp ==> Installing php dependency: apr ・ ・ ・ $ /usr/local/bin/php --version PHP 7.3.7 (cli) (built: Jul 5 2019 12:44:05) ( NTS ) Copyright (c) 1997-2018 The PHP Group Zend Engine v3.3.7, Copyright (c) 1998-2018 Zend Technologies with Zend OPcache v7.3.7, Copyright (c) 1999-2018, by Zend TechnologiesPHP新しくしたら このエラーは 起きなくなった。Cocの定義元移動は後編に続く。
- 投稿日:2019-07-30T13:56:25+09:00
PHPで生のリクエストボディーを読む
PHPではリクエストからパラメータを取得する場合
$_GET
や$_POST
が用意されていますが、これはContent-Type
がapplication/x-www-form-urlencoded
の場合の話です。
JSON-RPC
のようにContent-Type
がapplication/json
の場合にパラメーターを取得するにはPHPに頼らずリクエストボディーのJSONをPHPに頼らず自力でパースする必要があります。stream を使う
リクエストボディーを取得するにはWEBサーバとして動かす場合は
php://input
を使います。var_dump(file_get_contents('php://input'));下記を短く書いています。
$stream = fopen('php://input'); $data = stream_get_contents($stream); fclose($stream); var_dump($data);コード
リクエストボディーのJSONをパースしてダンプするスクリプトを
test.php
としてファイルにします。test.php<?php var_dump(json_decode(file_get_contents('php://input'), true));動作確認
PHPのビルトインサーバでホストし、curlでtest.phpを呼び出します。
$ php -S localhost:8080 # test.php の存在するディレクトリで実行する $ curl -X POST \ -d '{"hello":"world", "こんにちは":"世界"}' \ -H "Content-Type: application/json" \ http://localhost:8080/test.php array(2) { ["hello"]=> string(5) "world" ["こんにちは"]=> string(6) "世界" }例では
application/json
が送られてくる場合を仮定しましたが、application/x-www-form-urlencoded
でも、パースされていない生データを取得することができます。 (もちろんjson_decodeしてはいけません)ヒント
php://input
とphp://stdin
の違いがよくわかっていなくて、php://stdin
を使用すると、ブロッキングしてしまってWEBサーバがレスポンスを返さなくなります。CLIだとエンターを押すまでブロッキングするからでしょうかね。アプリケーションサーバとして使う場合は
php://input
を使いましょう。そしてブロッキングしてるWEBサーバは再起動しましょう。CLIの場合はSTDIN定数 (STDINは
fopen('php://stdin')
と等価です) やphp://stdin
を使うのだと思います。$HTTP_RAW_POST_DATA 変数を使う
PHP7 より前は
$HTTP_RAW_POST_DATA
に生のPOSTデータがセットされていましたが、PHP7以降では使えません。
- 投稿日:2019-07-30T13:54:52+09:00
Laravel 5.8 primaryKeyをint(AUTO_INCREMENT)からUUIDに変える
外部ライブラリを使う
$ composer require webpatser/laravel-uuid
Str::orderedUuidを使う(Laravel 5.6+)
Laravelのヘルパ関数だが外部ライブラリ
moontoast/math
が必要$ composer require moontoast/math
idの型を変える
こちらを参考に
[Laravel] テーブルのプライマリキーにuuidを使うdatabases/migrations/XXX_users_table.php- $table->bigIncrements('id'); + $table->uuid('id'); // string('id', 36)と同じ + $table->primary('id');モデルを修正
プライマリキーの名称を変える場合は、プロパティ
$primaryKey
で設定
型がデフォルトでintかつautoincrementのため、以下の定義を追加app/User.phpprotected $primaryKey = 'hoge_id' // 任意設定 public $incrementing = false; // AUTO_INCREMENT不可 protected $keyType = 'string'; // int -> stringレコード作成時にidを自動生成する処理を追加する。useを忘れないように。
app/User.php// webpatserの場合 use Illuminate\Database\Eloquent\Model; use Webpatser\Uuid\Uuid; // Str::orderedUuidの場合 use Illuminate\Support\Str; (略) protected static function boot() { parent::boot(); static::creating(function ($model) { // webpatserの場合 $model->{$model->getKeyName()} = (string) Uuid::generate()->string; // Str::orderedUuidの場合 $model->{$model->getKeyName()} = (string) Str::orderedUuid(); }); }Seeder経由で追加する場合
そのままではINSERTで失敗するのでidも自前で定義する
databases/seeders/UserTableSeeder.phpDB::table('users')->insert([ + 'id' => (string) Str::orderedUuid(), 'name' => 'admin', ]);
- 投稿日:2019-07-30T13:48:15+09:00
PHP簡易自作フレームワーク(version1)
自作フレームワーク実装例
非常に適当ですが、自分の頭の整理のために、開発途上を投稿します。
まずは、コントロールとビューを動的に読み込んでみました。/index.php<?php $controller=""; $action=""; $para=array(); if (!empty($_SERVER['PATH_INFO'])) { $para=explode('/',$_SERVER['PATH_INFO']); $controller=array_shift($para); if($controller==""){$controller=array_shift($para);} $action=array_shift($para); } if($controller=="" || $action=="" || substr($action,0,1)=="_"){ $fname='./views/default.php'; if(file_exists($fname)){ include($fname); }else{echo "file [$fname] not found. <br>";} exit; } $fname='./controllers/'.$controller.'.php'; if(file_exists($fname)){ include($fname); if(class_exists($controller)){ $class = new $controller(); if(method_exists($class,$action)){ $data = $class->$action($para); if(!is_array($data)){$data=array();} }else{echo "method [$action()] not found. <br>";} }else{echo "class [$controller] not found. <br>";} }else{echo "file [$fname] not found. <br>";} $fname='./views/'.$controller."_".$action.'.php'; if(file_exists($fname)){ include($fname); }else{echo "file [$fname] not found. <br>";}/controllers/abc.php<?php class abc{ function xyz(){ return array("head"=>"象が空を","body"=>"眺めていた。"); } }/views/abc_xyz.php<!DOCTYPE html> <html> <body> <h1><?php echo $data["head"]; ?></h1> <?php echo $data["body"]; ?> </body> </html>実行例
[URL] http://~/index.php/abc/xyz
参考
参考にさせていただきました。
https://qiita.com/kahirokunn/items/175b82295ab683ffb624
http://choilog.com/katty0324/blog/6
- 投稿日:2019-07-30T08:05:56+09:00
自分の勉強記録を分析しよう!~新米エンジニアの個人開発~
自分の勉強記録を分析しよう!~新米エンジニアの個人開発~
プログラマー歴1年目のじょーだいです。
プログラマーになって一年、そろそろ新しいことを学んでみたかったので、使ったことのない技術を使用して簡単なwebアプリケーションを開発してみました。
どんなアプリ?
僕は勉強したことをTwitterでつぶやいています。
他にも多くの方々が自分の勉強したことをつぶやいているのをよく見かけます。
せっかく勉強しているならその記録をしっかり残したい。。。
そこで今回は自分の勉強を記録しておくことのできる「Stacker」というアプリを開発しました。
使用している技術は?
- Laravel
- Vue.js
- Chart.js
- MySQL
- Docker
- XSERVER
- Git
各画面の説明
ダッシュボード
この画面の作成が一番時間をとられました。
上部に1週間のグラフ、下部に日毎の入力したデータが表示されます。
また右上のボタンによって、1週間のグラフを総合計のグラフに切り替えることでカテゴリーごとの合計時間を知ることができます。
勉強している日、してない日がすぐわかるので結構気に入ってます。
カテゴリ―毎の詳細
ダッシュボードの左側のカテゴリー一覧からカテゴリ―を選択すると画像のような画面が開きます。
今まで入力したコメントが一覧表示されるので、どんなことを勉強してきたのか一目瞭然です。
カテゴリ―詳細ページは他のユーザーが覗くこともできます。
知らない語句や勉強の順序、気になるエンジニアがどんなことを学んでいるのかを知ることができます。
ホーム
各ユーザーの投稿が一覧表示されています。
とても簡易的なSNSのような感じです。
フォロー機能等は実装していませんが、他のユーザーの勉強量を知ることで、モチベーションを上げることができます。
現在会社の後輩とこのアプリを使用しているのですが、とても会話が盛り上がります。
特に相手が何を勉強しているのかがわかるので、気になったことをすぐに聞けるし、話のきっかけづくりにもなります。
感想
一応完成はしたものの、まだまだ実装したい機能が盛りだくさんです。
例えば
フォロー機能
、円グラフでの分析
、カテゴリ―毎の表示順序、色の切り替え
など、他にも細かいところでまだまだたくさんあります。他にもリファクタリングやテストコードの作成などすることは盛りだくさん。
これからも勉強を記録していくためにこのアプリを使用し、改善を続けていきます。
自分自身が毎日使用していくので、改善、メンテナンスを無理なく続けていくことができると思います。
個人開発でも作りっぱなしではなく、しっかりと運用していくので、よければ皆様も使ってみてください。
一言
コードが汚すぎる。リーダブルコード読んだはずなんだけどな~
- 投稿日:2019-07-30T01:28:22+09:00
PHP 自分用まとめ 8
ジェネレータ
ジェネレータを使えば、シンプルな イテレータを簡単に実装できます
ジェネレータを使った range() の実装
function xrange($start, $limit, $step = 1) { if ($start < $limit) { if ($step <= 0) { throw new LogicException('Step must be +ve'); } for ($i = $start; $i <= $limit; $i += $step) { yield $i; } } else { if ($step >= 0) { throw new LogicException('Step must be -ve'); } for ($i = $start; $i >= $limit; $i += $step) { yield $i; } } } /* * 次の例で、range() と xrange() が同じ結果を返すことに注目しましょう */ echo 'Single digit odd numbers from range(): '; foreach (range(1, 9, 2) as $number) { echo "$number "; } echo "\n"; echo 'Single digit odd numbers from xrange(): '; foreach (xrange(1, 9, 2) as $number) { echo "$number "; } //上の例の出力は以下となります。 Single digit odd numbers from range(): 1 3 5 7 9 Single digit odd numbers from xrange(): 1 3 5 7 9ジェネレータの構文
yield キーワード
ジェネレータ関数の肝となるのが yield キーワードです。
最もシンプルな書きかたをすると、yield 文の見た目は return 文とほぼ同じになります。
ただ、return の場合はそこで関数の実行を終了して値を返すのに対して
yield の場合はジェネレータを呼び出しているループに値を戻して
ジェネレータ関数の実行を一時停止します。値を yield する単純な例
function gen_one_to_three() { for ($i = 1; $i <= 3; $i++) { // yield を繰り返す間、$i の値が維持されることに注目しましょう yield $i; } } $generator = gen_one_to_three(); foreach ($generator as $value) { echo "$value\n"; } //上の例の出力は以下となります。 1 2 3値とキーの yield
キー/値 のペアの yield
/* * 入力は各フィールドをセミコロンで区切ったものです * 最初のフィールドが ID となり、これをキーとして使います */ $input = <<<'EOF' 1;PHP;$が大好き 2;Python;インデントが大好き 3;Ruby;ブロックが大好き EOF; function input_parser($input) { foreach (explode("\n", $input) as $line) { $fields = explode(';', $line); $id = array_shift($fields); yield $id => $fields; } } foreach (input_parser($input) as $id => $fields) { echo "$id:\n"; echo " $fields[0]\n"; echo " $fields[1]\n"; } //上の例の出力は以下となります。 1: PHP $が大好き 2: Python インデントが大好き 3: Ruby ブロックが大好きnull 値の yield
function gen_three_nulls() { foreach (range(1, 3) as $i) { yield; } } var_dump(iterator_to_array(gen_three_nulls())); //上の例の出力は以下となります。 array(3) { [0]=> NULL [1]=> NULL [2]=> NULL }参照による値の yield
function &gen_reference() { $value = 3; while ($value > 0) { yield $value; } } /* * $number をループ内で変更していることに注目しましょう。 * このジェネレータは参照を yield するので、 * gen_reference() 内の $value が変わります。 */ foreach (gen_reference() as &$number) { echo (--$number).'... '; } //上の例の出力は以下となります。 2... 1... 0...yield from によるジェネレータの委譲
PHP 7 では、ジェネレータの委譲ができるようになりました。yield from の基本的な使いかた
function count_to_ten() { yield 1; yield 2; yield from [3, 4]; yield from new ArrayIterator([5, 6]); yield from seven_eight(); yield 9; yield 10; } function seven_eight() { yield 7; yield from eight(); } function eight() { yield 8; } foreach (count_to_ten() as $num) { echo "$num "; } //上の例の出力は以下となります。 1 2 3 4 5 6 7 8 9 10yield from の返す値
function count_to_ten() { yield 1; yield 2; yield from [3, 4]; yield from new ArrayIterator([5, 6]); yield from seven_eight(); return yield from nine_ten(); } function seven_eight() { yield 7; yield from eight(); } function eight() { yield 8; } function nine_ten() { yield 9; return 10; } $gen = count_to_ten(); foreach ($gen as $num) { echo "$num "; } echo $gen->getReturn(); //上の例の出力は以下となります。 1 2 3 4 5 6 7 8 9 10ジェネレータと Iterator オブジェクトとの比較
function getLinesFromFile($fileName) { if (!$fileHandle = fopen($fileName, 'r')) { return; } while (false !== $line = fgets($fileHandle)) { yield $line; } fclose($fileHandle); } // これを、下のクラスと比べてみると…… class LineIterator implements Iterator { protected $fileHandle; protected $line; protected $i; public function __construct($fileName) { if (!$this->fileHandle = fopen($fileName, 'r')) { throw new RuntimeException('Couldn\'t open file "' . $fileName . '"'); } } public function rewind() { fseek($this->fileHandle, 0); $this->line = fgets($this->fileHandle); $this->i = 0; } public function valid() { return false !== $this->line; } public function current() { return $this->line; } public function key() { return $this->i; } public function next() { if (false !== $this->line) { $this->line = fgets($this->fileHandle); $this->i++; } } public function __destruct() { fclose($this->fileHandle); } }リファレンスの説明
リファレンスとは同じ変数の内容を異なった名前で コールすることを意味します。
これは C のポインタとは異なります。
リファレンスを使ってポインタの演算をすることはできませんし
リファレンスは実メモリのアドレスでもありません。リファレンスはシンボルテーブルのエイリアスです。
リファレンスの代入
$a =& $b; //この場合、$a と $b は同じ内容を指します。 //ここで、$a と $b は完全に 同じで //$a が $b を 指しているわけではなく、その逆でもありません。 //$a と $b は同じ場所を指しているのです。厳密にリファレンスでの代入をしなくても
array() で作成した式は配列の要素の先頭に & を追加したのと同じような動作をします。
たとえば次のようになります。$a = 1; $b = array(2, 3); $arr = array(&$a, &$b[0], &$b[1]); $arr[0]++; $arr[1]++; $arr[2]++; /* $a == 2, $b == array(3, 4); */しかし、配列の内部のリファレンスは危険もあるということに気をつけましょう。
/* スカラー変数への代入 */ $a = 1; $b =& $a; $c = $b; $c = 7; // $c はリファレンスではないので $a や $b の値は変わりません /* 配列変数への代入 */ $arr = array(1); $a =& $arr[0]; // $a および $arr[0] は同じリファレンスセットになります $arr2 = $arr; // これは、リファレンス代入ではありません! $arr2[0]++; /* $a == 2, $arr == array(2) */ /* $arr の内容は、たとえリファレンスでなくても変わります! */リファレンス渡し
function foo(&$var) { $var++; } $a=5; foo($a); //この結果、$a は 6 となります。 //これは、関数 foo の中では、変数 $var は $a と同じ内容を指しているためです。リファレンスが行わないこと
リファレンスはポインタではありません。このため次の例は期待通りに動作しません。
function foo(&$var) { $var =& $GLOBALS["baz"]; } foo($bar);リファレンス渡し
リファレンスにより関数に変数を渡すことが可能です。
この場合、関数内で その引数を修正可能になります。function foo(&$var) { $var++; } $a=5; foo($a); // $a はここでは 6 です次のものはリファレンスで渡すことが可能です。
変数、すなわち、foo($a)
new 命令、すなわち、foo(new foobar())
関数から返されるリファレンスは、次のようになります。function foo(&$var) { $var++; } function &bar() { $a = 5; return $a; } foo(bar());他の式は、結果が未定義となるため、リファレンスで渡すべきではありません。
function foo(&$var) { $var++; } function bar() // & がないことに注意 { $a = 5; return $a; } foo(bar()); // PHP 5.0.5 以降、致命的なエラーが発生する // PHP 5.1.1 以降では strict notice、そして PHP 7.0.0 以降では notice となる foo($a = 5); // 式、変数ではない foo(5); // 致命的なエラーが発生するリファレンスを返す
class foo { public $value = 42; public function &getValue() { return $this->value; } } $obj = new foo; $myValue = &$obj->getValue(); // $myValue は $obj->value へのリファレンス、つまり 42 となります $obj->value = 2; echo $myValue; // $obj->value の新しい値である 2 を表示します //この例では、関数 getValue により返された オブジェクトのプロパティが、設定されます。 //リファレンス構文を 使用しない場合のようにコピーとなるわけではありません。返されたリファレンスを使うには、リファレンスを代入しなければなりません。
function &collector() { static $collection = array(); return $collection; } $collection = &collector(); $collection[] = 'foo';返されたリファレンスを、リファレンスを要求する別の関数に渡すには、 この構文を使います。
function &collector() { static $collection = array(); return $collection; } array_push(collector(), 'foo');リファレンスの解除
$a = 1; $b =& $a; unset($a); //は、$b を削除せず、$a のみを 削除します。リファレンスの適用範囲
global リファレンス
変数を global $var として宣言した場合
実際には グローバル変数へのリファレンスを作成したことになります。$var =& $GLOBALS["var"]; //これは、例えば、$var を削除してもグローバル変数は 削除されないことを意味します。
- 投稿日:2019-07-30T00:50:00+09:00
PHP 自分用まとめ 7
名前空間
名前空間の宣言
namespace MyProject; const CONNECT_OK = 1; class Connection { /* ... */ } function connect() { /* ... */ }名前空間の宣言より前に書くことが許されているコードは declare 文のみです。
<html> <?php namespace MyProject; // fatal error - namespace must be the first statement in the script ?>サブ名前空間の宣言
階層つきの名前空間の宣言
namespace MyProject\Sub\Level; const CONNECT_OK = 1; class Connection { /* ... */ } function connect() { /* ... */ } //上の例は、 //定数 MyProject\Sub\Level\CONNECT_OK と //クラス MyProject\Sub\Level\Connection、 //そして関数 MyProject\Sub\Level\connect を作成します。同一ファイル内での複数の名前空間の定義
シンプルな組み合わせ方式による複数の名前空間の宣言
namespace MyProject; const CONNECT_OK = 1; class Connection { /* ... */ } function connect() { /* ... */ } namespace AnotherProject; const CONNECT_OK = 1; class Connection { /* ... */ } function connect() { /* ... */ } //この構文は、複数の名前空間をひとつのファイルに含める場合の方法としてはお勧めしません。 //かわりに、次の波括弧構文を使うことを推奨します。波括弧構文による複数の名前空間の宣言
namespace MyProject { const CONNECT_OK = 1; class Connection { /* ... */ } function connect() { /* ... */ } } namespace AnotherProject { const CONNECT_OK = 1; class Connection { /* ... */ } function connect() { /* ... */ } }複数の名前空間および名前空間に属さないコードの宣言
namespace MyProject { const CONNECT_OK = 1; class Connection { /* ... */ } function connect() { /* ... */ } } namespace { // グローバルコード session_start(); $a = MyProject\connect(); echo MyProject\Connection::start(); }namespace の波括弧の外側に書くことができる PHP コードは、最初の declare 文だけです。
複数の名前空間および名前空間に属さないコードの宣言declare(encoding='UTF-8'); namespace MyProject { const CONNECT_OK = 1; class Connection { /* ... */ } function connect() { /* ... */ } } namespace { // global code session_start(); $a = MyProject\connect(); echo MyProject\Connection::start(); }名前空間の使用法: 基本編
これら 3 つの構文を実際のコードで使う例を次に示します。
file1.php
<?php namespace Foo\Bar\subnamespace; const FOO = 1; function foo() {} class foo { static function staticmethod() {} } ?>file2.php
<?php namespace Foo\Bar; include 'file1.php'; const FOO = 2; function foo() {} class foo { static function staticmethod() {} } /* 非修飾名 */ foo(); // Foo\Bar\foo 関数と解釈されます foo::staticmethod(); // Foo\Bar\foo クラスの staticmethod メソッドと解釈されます echo FOO; // 定数 Foo\Bar\FOO と解釈されます /* 修飾名 */ subnamespace\foo(); // Foo\Bar\subnamespace\foo 関数と解釈されます subnamespace\foo::staticmethod(); // Foo\Bar\subnamespace\foo クラスの // staticmethod メソッドと解釈されます echo subnamespace\FOO; // 定数 Foo\Bar\subnamespace\FOO と解釈されます /* 完全修飾名 */ \Foo\Bar\foo(); // Foo\Bar\foo 関数と解釈されます \Foo\Bar\foo::staticmethod(); // Foo\Bar\foo クラスの staticmethod メソッドと解釈されます echo \Foo\Bar\FOO; // 定数 Foo\Bar\FOO と解釈されます ?>グローバルなクラス、関数あるいは定数にアクセスするには
完全修飾名を使用して \strlen()、\Exception あるいは \INI_ALL などとすることができます。グローバルなクラス、関数および定数への名前空間内からのアクセス
namespace Foo; function strlen() {} const INI_ALL = 3; class Exception {} $a = \strlen('hi'); // グローバル関数 strlen をコールします $b = \INI_ALL; // グローバル定数 INI_ALL にアクセスします $c = new \Exception('error'); // グローバルクラス Exception のインスタンスを作成します名前空間と動的言語機能
次の例のようなコードを名前空間を使って書き直すには要素への動的なアクセス
example1.php:
<?php class classname { function __construct() { echo __METHOD__,"\n"; } } function funcname() { echo __FUNCTION__,"\n"; } const constname = "global"; $a = 'classname'; $obj = new $a; // classname::__construct と表示します $b = 'funcname'; $b(); // funcname と表示します echo constant('constname'), "\n"; // global と表示します ?>名前空間つき要素への動的なアクセス
<?php namespace namespacename; class classname { function __construct() { echo __METHOD__,"\n"; } } function funcname() { echo __FUNCTION__,"\n"; } const constname = "namespaced"; /* ダブルクォートを使う場合は "\\namespacename\\classname" としなければなりません */ $a = '\namespacename\classname'; $obj = new $a; // namespacename\classname::__construct と表示します $a = 'namespacename\classname'; $obj = new $a; // これも namespacename\classname::__construct と表示します $b = 'namespacename\funcname'; $b(); // namespacename\funcname と表示します $b = '\namespacename\funcname'; $b(); // これも namespacename\funcname と表示します echo constant('\namespacename\constname'), "\n"; // namespaced と表示します echo constant('namespacename\constname'), "\n"; // これも namespaced と表示します ?><?php namespace MyProject; echo '"', __NAMESPACE__, '"'; // "MyProject" と出力します ?> //名前空間内のコードでの __NAMESPACE__ の例<?php echo '"', __NAMESPACE__, '"'; // "" と出力します ?> //グローバルなコードでの __NAMESPACE__ の例<?php namespace MyProject; function get($classname) { $a = __NAMESPACE__ . '\\' . $classname; return new $a; } ?> //__NAMESPACE__ による動的な名前の作成名前空間内での namespace 演算子
<?php namespace MyProject; use blah\blah as mine; // "名前空間の使用法: エイリアス/インポート" を参照ください blah\mine(); // MyProject\blah\mine() 関数をコールします namespace\blah\mine(); // MyProject\blah\mine() 関数をコールします namespace\func(); // MyProject\func() 関数をコールします namespace\sub\func(); // MyProject\sub\func() 関数をコールします namespace\cname::method(); // MyProject\cname クラスの静的メソッド "method" をコールします $a = new namespace\sub\cname(); // MyProject\sub\cname クラスのオブジェクトのインスタンスを作成します $b = namespace\CONSTANT; // 定数 MyProject\CONSTANT の値を $b に代入します ?>グローバルコードでの namespace 演算子
<?php namespace\func(); // func() 関数をコールします namespace\sub\func(); // sub\func() 関数をコールします namespace\cname::method(); // cname クラスの静的メソッド "method" をコールします $a = new namespace\sub\cname(); // sub\cname クラスのオブジェクトのインスタンスを作成します $b = namespace\CONSTANT; // 定数 CONSTANT の値を $b に代入します ?>名前空間の使用法: エイリアス/インポート
PHP でのエイリアス作成には use 演算子を使用します。
use 演算子によるインポート/エイリアス
<?php namespace foo; use My\Full\Classname as Another; // これは use My\Full\NSname as NSname と同じです use My\Full\NSname; // グローバルクラスをインポートします use ArrayObject; // 関数をインポートします (PHP 5.6+) use function My\Full\functionName; // 関数のエイリアスを定義します (PHP 5.6+) use function My\Full\functionName as func; // 定数をインポートします (PHP 5.6+) use const My\Full\CONSTANT; $obj = new namespace\Another; // foo\Another クラスのオブジェクトのインスタンスを作成します $obj = new Another; // My\Full\Classname クラスのオブジェクトのインスタンスを作成します NSname\subns\func(); // My\Full\NSname\subns\func 関数をコールします $a = new ArrayObject(array(1)); // ArrayObject クラスのオブジェクトのインスタンスを作成します // "use ArrayObject" がなければ、foo\ArrayObject クラスのオブジェクトのインスタンスを作成することになります func(); // 関数 My\Full\functionName を呼びます echo CONSTANT; // 定数 My\Full\CONSTANT の値を表示します ?>use 演算子によるインポート/エイリアスで、複数の use 文を組み合わせる例
<?php use My\Full\Classname as Another, My\Full\NSname; $obj = new Another; // My\Full\Classname クラスのオブジェクトのインスタンスを作成します NSname\subns\func(); // My\Full\NSname\subns\func 関数をコールします ?>インポートと動的名
<?php use My\Full\Classname as Another, My\Full\NSname; $obj = new Another; // My\Full\Classname クラスのオブジェクトのインスタンスを作成します $a = 'Another'; $obj = new $a; // Another クラスのオブジェクトのインスタンスを作成します ?>インポートと完全修飾名
<?php use My\Full\Classname as Another, My\Full\NSname; $obj = new Another; // My\Full\Classname クラスのオブジェクトのインスタンスを作成します $obj = new \Another; // Another クラスのオブジェクトのインスタンスを作成します $obj = new Another\thing; // My\Full\Classname\thing クラスのオブジェクトのインスタンスを作成します $obj = new \Another\thing; // Another\thing クラスのオブジェクトのインスタンスを作成します ?>use 宣言のグループ化
<?php // 以前のバージョンのコード use some\namespace\ClassA; use some\namespace\ClassB; use some\namespace\ClassC as C; use function some\namespace\fn_a; use function some\namespace\fn_b; use function some\namespace\fn_c; use const some\namespace\ConstA; use const some\namespace\ConstB; use const some\namespace\ConstC; // PHP 7 以降のコード use some\namespace\{ClassA, ClassB, ClassC as C}; use function some\namespace\{fn_a, fn_b, fn_c}; use const some\namespace\{ConstA, ConstB, ConstC};グローバル空間
名前空間の定義がない場合、すべてのクラスや関数の定義はグローバル空間に配置されます。
名前の先頭に \ をつけると
名前空間の内部からであってもグローバル空間の名前を指定することができます。グローバル空間を指定する方法
<?php namespace A\B\C; /* この関数は A\B\C\fopen です */ function fopen() { /* ... */ $f = \fopen(...); // グローバルな fopen をコールします return $f; } ?>名前空間の使用法: グローバル関数/定数への移行
名前空間内からのグローバルクラスへのアクセス
<?php namespace A\B\C; class Exception extends \Exception {} $a = new Exception('hi'); // $a は A\B\C\Exception クラスのオブジェクトです $b = new \Exception('hi'); // $b は Exception クラスのオブジェクトです $c = new ArrayObject; // fatal error, class A\B\C\ArrayObject not found ?>関数や定数の場合
名前空間内にその関数や定数が見つからなければ PHP はグローバル関数/定数を探します。名前空間内からのグローバル関数/定数への移行
<?php namespace A\B\C; const E_ERROR = 45; function strlen($str) { return \strlen($str) - 1; } echo E_ERROR, "\n"; // "45" と表示します echo INI_ALL, "\n"; // "7" と表示します - グローバルの INI_ALL に移行しました echo strlen('hi'), "\n"; // "1" と表示します if (is_array('hi')) { // "is not array" と表示します echo "is array\n"; } else { echo "is not array\n"; } ?>名前解決の例
<?php namespace A; use B\D, C\E as F; // 関数のコール foo(); // まず名前空間 "A" で定義されている "foo" のコールを試み、 // 次にグローバル関数 "foo" をコールします \foo(); // グローバルスコープで定義されている関数 "foo" をコールします my\foo(); // 名前空間 "A\my" で定義されている関数 "foo" をコールします F(); // まず名前空間 "A" で定義されている "F" のコールを試み、 // 次にグローバル関数 "F" をコールします // クラスの参照 new B(); // 名前空間 "A" で定義されているクラス "B" のオブジェクトを作成します // 見つからない場合は、クラス "A\B" の autoload を試みます new D(); // インポートルールを使用し、名前空間 "B" で定義されているクラス "D" のオブジェクトを作成します // 見つからない場合は、クラス "B\D" の autoload を試みます new F(); // インポートルールを使用し、名前空間 "C" で定義されているクラス "E" のオブジェクトを作成します // 見つからない場合は、クラス "C\E" の autoload を試みます new \B(); // グローバルスコープで定義されているクラス "B" のオブジェクトを作成します // 見つからない場合は、クラス "B" の autoload を試みます new \D(); // グローバルスコープで定義されているクラス "D" のオブジェクトを作成します // 見つからない場合は、クラス "D" の autoload を試みます new \F(); // グローバルスコープで定義されているクラス "F" のオブジェクトを作成します // 見つからない場合は、クラス "F" の autoload を試みます // 別の名前空間から使用する静的メソッド/関数 B\foo(); // 名前空間 "A\B" の関数 "foo" をコールします B::foo(); // 名前空間 "A" で定義されているクラス "B" のメソッド "foo" をコールします // クラス "A\B" が見つからない場合はクラス "A\B" の autoload を試みます D::foo(); // インポートルールを使用し、名前空間 "B" で定義されているクラス "D" のメソッド "foo" をコールします // クラス "B\D" が見つからない場合はクラス "B\D" の autoload を試みます \B\foo(); // 名前空間 "B" の関数 "foo" をコールします \B::foo(); // グローバルスコープのクラス "B" のメソッド "foo" をコールします // クラス "B" が見つからない場合はクラス "B" の autoload を試みます // 現在の名前空間から使用する静的メソッド/関数 A\B::foo(); // 名前空間 "A\A" のクラス "B" のメソッド "foo" をコールします // クラス "A\A\B" が見つからない場合はクラス "A\A\B" の autoload を試みます \A\B::foo(); // 名前空間 "A" のクラス "B" のメソッド "foo" をコールします // クラス "A\B" が見つからない場合はクラス "A\B" の autoload を試みます ?>