カテゴリーアーカイブ PHP

takahashi 著者:takahashi

FuelPHPで今開いているページのコントローラー/アクションを取得する方法

・コントローラー名を取得したいとき(コントローラークラス名)

echo Request::main()->controller;
//ex.: Controller_Hogehoge

・アクション名を取得したいとき

echo Request::active()->action:
//ex.: huga

なお、コントローラーを取る方法として、Uriクラスを使って

 echo Request::main()->uri->segment(1);  //ベースURLから1番目の値をとる

とすることもできるようですが、index(/)などの場合、取得できる値がNULLになってしまう問題があるため、個人的には最初の方法で取得したうえで正規表現などで加工した方が確実かな、と思います。

参考:
[PHP][FuelPHP]FuelPHPで気になるあの情報の取り出し方 – ECサイト運営開発記

  • この記事いいね! (0)
著者:杉浦

array_uniqueとsuper_unique

 PHPにはarray_uniqueという組み込み関数があります。
PHP: array_unique – Manual
 効果は次の引用の通りシンプルで配列の中身をユニークなものにします。

array_unique
( array $array
[, int $sort_flags = SORT_STRING
] ) : array

array を入力とし、値に重複のない新規配列を返します。

引用:PHP: array_unique – Manual

 この第二引数sort_flagsはsortなどとついていますが、実質的にcompare_flagsともいうべきものです。array_uniqueは次の通りに配列の中身の値を扱って比較します。

  • SORT_REGULAR – 通常の比較
    (型変換をしない) を行います
  • SORT_NUMERIC – 数値として比較します
  • SORT_STRING – 文字列として比較します
  • SORT_LOCALE_STRING
    現在のロケールにもとづいて文字列として比較します。

引用:PHP: array_unique – Manual

 ちなみにデフォルトはSORT_STRINGです。数値、文字列以外はSORT_REGULARでそれっぽく動きます。

注意:

array_unique() は、
多次元配列での使用を想定したものではないことに注意しましょう。

引用:PHP: array_unique – Manual

 それっぽいといった通り、多重配列であったり配列の中身の型がまちまちだった場合、array_uniqueは意図しない挙動を起こしだします。この問題は昔からあった様で解決策はPHP: array_unique – Manual #97285にありました。

recursive array unique for multiarrays

<?php
function super_unique($array)
{
  $result = array_map("unserialize", array_unique(array_map("serialize", $array)));

  foreach ($result as $key => $value)
  {
    if ( is_array($value) )
    {
      $result[$key] = super_unique($value);
    }
  }

  return $result;
}

 やっていることは配列の中身をserialize(値の保存可能な表現を生成)し、配列の中身を一旦全て文字列に変換、array_uniqueを実行、unserializeで元の型に復元、です。このsuper_uniqueを用いることでより直感的なarray_uniqueの挙動を実現できます。

  • この記事いいね! (0)
著者:杉浦

PHPStormでartisanコマンドの予測入力をする

 PHPフレームワークLaravelにはartisanコマンドがあります。artisanはアプリケーション開発全体で役に立つ、数多くのコマンドを提供しています。例えば必須ないしあった方がよいコードを持ったモデル、テスト、ミドルウェアの作成、データベースのまとまった操作、Laravelが今持つことになるルーティングリストの表示です。もちろん他にも様々なものがあります。
 artisanの一覧を見ることはphp artisan listからできますが、いちいち確認するのは面倒です。PHPStormのコマンドラインツールにはSymfonyのコマンドライン用クラス\Symfony\Component\Console\Command\Commandを元に作成されたコマンドのコマンドライン入力とサポートするプラグインCommand Line Tool Supportがあります。LaravelのartisanコマンドはSymfonyのCommandクラスから継承されて作られています。次の図はCommandクラスの継承先のリストの一部です。各aritsanコマンドの名が冠されたクラスが並んでいます。
 
 プラグインは設定->プラグインのマーケットプレイスから検索できます。インストールできたなら設定->ツール->コマンド・ライン・ツールのサポートを選び、追加からTool Based on Symfony Consoleを選択、php実行ファイルとartisanファイルのパスを設定して完成です。


 これでartisanコマンドの予測ができるようになりました。artisanを介することでnamespaceや必須メソッド、継承元などをいちいち記述せずとも各基本クラスのひな型を作ることができ、データベースの操作もまとめてでき、開発がはかどります。

  • この記事いいね! (0)
村上 著者:村上

【PHP】Slim3フレームワークのCSRF対策ミドルウェア「Slim-Csrf」の導入方法

勘違いから若干手こずったので、備忘録としてまとめ。
PHPフレームワーク「Slim3」でCSRF対策を行うためのミドルウェア「Slim-Csrf」の導入方法です。

GitHub のページはこちらから。

GitHub – slimphp/Slim-Csrf: Slim Framework CSRF protection middleware
https://github.com/slimphp/Slim-Csrf

サンプルコードも掲載されているので、分かりやすいはずなのですが…よくわからない勘違いをしたせいで、少し悩んでしまいました。

 

では、実装方法手順について。
なお、開発環境は「Slim-Skeleton」で作成したプロジェクトを使用しています。

まず下記のコマンドを実行して「Slim-Csrf」をインストールします。

composer require slim/csrf

インストールが完了したら、src/middleware.php を開き、下記のコードを追加します。

$container['csrf'] = function ($c) {
    return new \Slim\Csrf\Guard;
};
$app->add($container->get('csrf'));

上記を追加したら、次は src/routes.php を開いて、まずは下記のコードを追加します。

$app->get('/[任意のパス]', function (Request $request, Response $response, array $args) {
    $args['csrf_name'] = $request->getAttribute('csrf_name');
    $args['csrf_value'] = $request->getAttribute('csrf_value');

    // Render index view
    return $this->renderer->render($response, 'index.phtml', $args);
    // 任意のテンプレートに変更してもOK
});

こちらは、送信元のページです。
2、3行目のように、変数 $args に値を格納すれば、6行目で指定しているテンプレートでその値をしようできます。
上記コードでは、csrf_namecsrf_value に格納するランダムな値を生成し、それをテンプレートに渡しています。

次は、テンプレートファイルを開きます。
私の場合は、templates/index.phtml を使用します。
追加するコードは下記のとおり。なお、Form 部分だけを抜粋しています。


<form action="[送信先のパス]" method="POST" enctype="multipart/form-data">
    <input type="hidden" name="csrf_name" value="<?=$csrf_name ?>">
    <input type="hidden" name="csrf_value" value="<?=$csrf_value ?>">
    <input type="file" name="picture" accept="image/*">
    <input type="submit" name="submit" value="送信">
</form>

重要なのが 2、3行目で、上の src/routes.php で生成した変数 csrf_name と csrf_value の値を、同名の input タグ(hidden)の value に格納しています。
これらの値を一緒に送ることで、他サイトから等の不正な書き込みを検知・防止することができます。

最後に、送信先の処理を、src/routes.php に記述します。

$app->post('[送信先のパス]', function (Request $request, Response $response, array $args) {
    $postParams = $request->getParsedBody();
    $uploadedFiles = $request->getUploadedFiles();
    $picture = $uploadedFiles['picture'];
    if ($picture->getError() === UPLOAD_ERR_OK) {
        // 送信されたファイルに行う処理
    }
});

といっても、生成したランダムな値のチェックは自動でやってくれるので、成功した場合は上記で記述した処理が実行されるようになっています。
そのため、こちらには送信したデータの処理内容だけを書けばOKです。

上記のコードでは、Form で画像データを送っているので、その送信されたファイルデータを取得しています。
アップロード等は、適宜行ってください。
なお、Slim3 のドキュメントにもファイルのアップロードに関する項目がありますので、参考にしてください。
リンクはこちら。

Uploading files using POST forms – Slim Framework
http://www.slimframework.com/docs/v3/cookbook/uploading-files.html

必要なコードは以上です。

 

以上、「Slim-Csrf」ミドルウェアを使用した、CSRF対策でした。
Slim-Skeleton で作成したプロジェクトがベースになっていますが、恐らく流用はできるはず。
参考にしていただければ幸いです。

  • この記事いいね! (0)
村上 著者:村上

【PHP】Slim3初心者に!プロジェクトのひな型を作成できるパッケージ「slim/slim-skeleton」

昨日の記事で、Slim3の導入方法について紹介しましたが、「slim/slim-skeleton」パッケージを使った方が、特に初心者にはわかりやすいと思ったので、そのご紹介です。
私はテンプレートの導入方法で若干詰みかけていたのですが、このパッケージで作成したプロジェクトには既にテンプレートファイルが導入済みだったので、とてもありがたかったです。

参考にさせていただいた記事はこちらから。

Slim3のスケルトンプロジェクト、slim/slim-skeletonの解説 – akamist blog
https://akamist.com/blog/archives/815

主要なファイルを全て解説してあって、大変分かりやすかったです。
初心者にはおすすめの記事ですね。

GitHub のページはこちらから。

GitHub – slimphp/Slim-Skeleton: Slim Framework 3 skeleton application
https://github.com/slimphp/Slim-Skeleton

 

プロジェクトの新規作成には、下記のコマンドを実行します。

composer create-project slim/slim-skeleton [プロジェクト名]

あとは放っておけば、プロジェクトが作成されるので、コマンドが終了したら下記を実行。
なお、cd コマンドで、作成したプロジェクトのルートディレクトリに移動しておくのをお忘れなく。

php -S localhost:8080 -t public public/index.php
// または
composer start

必要なファイルは自動的に生成されるので、詳細についてはコードを参照ください。
なお、ルーティングファイルとして src/routes.php はありますが、コントローラーファイル等はなく、パスごとの処理は src/routes.php ファイルに直書きされているので、状況に応じてコントローラーファイルを作成し、処理を分けてください。

また、機能の記事にも書きましたが、Slim3 は必要最低限の機能しかないため、CSRF 対策を行いたい場合は、別途「slimphp/Slim-Csrf」を導入してください。
なお、ミドルウェアの導入は、src/middleware.php に記述します。
コメントにサンプルコードが書かれているので、参考にしてください。

 

以上、プロジェクトのひな型を作成してくれる「slim/slim-skeleton」パッケージのご紹介でした。
近いうちに「slimphp/Slim-Csrf」の紹介もしたいと思います。

なお、あまり参考になるかは分かりませんが、昨日の記事はこちらから。

【PHP】軽量フレームワーク「Slim3」の導入方法
https://cpoint-lab.co.jp/article/201901/【php】軽量フレームワーク「slim3」の導入方法/

紹介しているリンク先の記事はとても参考になりますので、ご活用ください。

  • この記事いいね! (0)
村上 著者:村上

【PHP】軽量フレームワーク「Slim3」の導入方法

現在、PHPの軽量フレームワークで「Slim3」というものを試してみています。
こちらのフレームワークは必要最低限の機能しかないため軽量で、学習コストも低くて済みます。
当然、基本機能以外は開発者が実装しなければいけませんが、その分カスタマイズ性が高いという利点もあります。

ちなみに、この「Slim3」を選んだ理由は、「PHP フレームワーク 軽量」と調べたときに、候補の一番最初に表示されたからという理由です。
でも、触ってみた感じは、(今のところ)なかなか分かりやすくておすすめですね。

今回参考にさせていただいた記事はこちら。

PHP軽量FrameworkのSlim3 – Qiita
https://qiita.com/Syo_pr/items/b55e18a8361b3ff882b5

こちらの内容に沿って、実装を行いました。

 

まず、ディレクトリを作成します。
名前は任意でOKですが、私はミス防止に、サンプルの名前をそのまま使っています。

mkdir testSlim
cd testSlim/
mkdir public
cd public/

ディレクトリをつくったら、次に index.php を用意します。

vim index.php

index.php の内容はこちら。

<?php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;

require __DIR__ . '/../vendor/autoload.php';

$app = new \Slim\App;
$app->get('/hello/{name}', function (Request $request, Response $response) {
    $name = $request->getAttribute('name');
    $response->getBody()->write("Hello, $name");

    return $response;
});
$app->run();

最後に、一つ上のルートディレクトリに戻り、下記を実行します。

composer require slim/slim "^3.0"

なお、環境によっては Composer コマンドが使えないこともあるので、その場合は Composer をインストールします。
案の定、私の環境では使えなかったため、下記のサイトを参考にしてインストールを行いました。

Composer を Windows にインストールする手順|WEB ARCH LABO
https://weblabo.oscasierra.net/php-composer-windows-install/

インストール後、composer -V を実行して、バージョンが表示されれば成功です。

そして、下記のコマンドを実行し、ビルトインウェブサーバーを起動します。

php -S localhost:8080 -t public public/index.php

サンプル通りに作成した場合は、http://localhost:8080/hello/test にアクセスし、Hello, test と表示されれば成功です。

上で紹介した参考サイトでは、Routes.php とコントローラーファイルを作成し、Laravel 風のルーティングテーブルを作成する方法についても記述されていました。
こちらを導入すると、index.php や Routes.php がすっきりするため、おすすめです。

 

なお、Slim3 は最低限の機能しかないため、CSRF 対策を行う場合は、別途 slimphp/Slim-Csrf を導入する必要があります。
GitHub のページはこちらから。

GitHub – slimphp/Slim-Csrf: Slim Framework CSRF protection middleware
https://github.com/slimphp/Slim-Csrf

インストール方法から導入の手順まで載っているので、是非参考にしてください。
slimphp/Slim-Csrf の導入については、後日記事にまとめたいと思っています。

 

以上、軽量フレームワーク「Slim3」の紹介と導入方法でした。
現在は、slimphp/Slim-Csrf の導入の途中まではできたので、引き続き開発を行っていきたいです。
テンプレートを用意して、それを読み込んだりもしたいですね。
公式のドキュメントでは、slim/twig-view が紹介されていたので、こちらを導入してみたいと思います。

  • この記事いいね! (0)
著者:杉浦

ダミー画像表示サイトlorempixelの紹介

lorempixel – placeholder images for every case
 lorempixelはurl指定するとダミーの画像を表示してくれるサイトです。urlの指定の仕方は次の画像の通り。urlを指定すればするほどオンリーワンな画像が表示されます。例はhttpプロトコルですがhttpsでもアクセスできます。

 例えば、https://lorempixel.com/400/200/animals/5/msgならば次の様に、横幅400px、縦幅200px、カテゴリanimalsの5番目の画像の左下にmsgの文字を入れた画像が返ってきます。

 webページを制作した時の画像確認においてカテゴリ分けが特に便利で仮のそれらしい画像をいくつも作る手間が省かれます。
 PHPのダミーデータ作成ライブラリfzaninotto/Faker: Faker is a PHP library that generates fake data for youにはlorempixelが組み込まれています

// Image generation provided by LoremPixel (http://lorempixel.com/)
imageUrl($width = 640, $height = 480) // 'http://lorempixel.com/640/480/'
imageUrl($width, $height, 'cats')     // 'http://lorempixel.com/800/600/cats/'
imageUrl($width, $height, 'cats', true, 'Faker') // 'http://lorempixel.com/800/400/cats/Faker'
imageUrl($width, $height, 'cats', true, 'Faker', true) // 'http://lorempixel.com/grey/800/400/cats/Faker/' Monochrome image

 Fakerが組み込まれているLaravelならばダミーデータ作成機能ファクトリに次の様にランダムURL生成を定義できます。

use Faker\Generator as Faker;

// あらかじめ使用する予定のカテゴリを定義
$category = ['abstract', 'animals', 'business', 'cats', 'city', 'food', 'nightlife', 'fashion', 'people', 'nature', 'sports', 'technics', 'transport'];
/* @var \Illuminate\Database\Eloquent\Factory $factory */
$factory->define(App\Models\Post::class, function (Faker $faker) use ($category) {
    $word = $faker->word; // 画像に記述する文字列をランダム生成
    return [
        'img_url' =>
            $faker->imageUrl(640, 480, $faker->randomElement($category), false) // https://lorempixel.com/640/480/{$categoryのどれか}/
            . $faker->numberBetween(1, 10) . '/' . $word, // 末尾に、1~10のいずれかと書き込む$wordを追加
    ];
});
  • この記事いいね! (0)
著者:杉浦

duskによるテストの開始時、終了時にLaravel内で定義してある関数を用いる

 Laravel内で共通のことですがクラス中で用いる初期化関数__construct()が上手く働かないことがあります。これに陥る原因としてLaravel内でクラスを呼び出した時、まだインスタンス化されていないクラスに関わる関数を呼び出すというものがあります。Laravelの組み込み関数や定義したEloquentによる関数、プロパティは便利ですが__construct()内では使えません。duskも同様です。
 Laravel内で用いられているクラスはこれを解決するために、前処理が終わった後、本処理が始まる前に呼び出される関数をboot,setupなどといった名前で定義しています。__construct()の次にboot()が走り、boot()の次にコントローラなどの本処理が走るといった具合です。duskではsetUp()という名前で定義されています。このsetUp()関数内でテストに使うデータを定義すると幾分かすんなりとテストを記述できます。例えば次です。

    /**
     * @inheritDoc
     */
    protected function setUp()
    {
        parent::setUp();
        // テストに用いる編集対象の管理者を指定、インスタンス化
        $admin_key = static::$admin_key ?? Admins::inRandomOrder()->first()->getKey();
        $this->admin = Admins::findOrFail($admin_key);
    }

 あらかじめ特定の管理者を対象にするならばその管理者を、そうでないならばランダムに選んだ管理者を対象にして様々なテストに用います。
 開始に対応するように終了時にフックされる関数もあります。dusk内ではtearDownとされています。あるテストが終わるたびにtearDown関数が呼ばれているため、ブラウザテストの完全終了を待たずともあるテストケースの失敗とそれを解決するための情報を得る記述をすることが出来ます。tests/DuskTestCase.phpを継承することで各テストケースがブラウザ上で走るため、tests/DuskTestCase内に次の様に記述するのが良いでしょう。DuskTestCaseより深く潜るとvendorの領域に入ってしまいます。

    /**
     * @inheritDoc
     */
    protected function tearDown()
    {
        parent::tearDown();
        if ($this->hasFailed()) {
            dump('failed in ' . static::class . '::' . $this->getName() . '. test case instance is');
            $test_class_data = (new \Illuminate\Support\Collection($this))->filter(function ($value, $key) {
                return !strpos($key, 'PHPUnit');// 2、3000行程になるPHPUnit自体の情報を削除
            });
            dump($test_class_data);
        }
    }
  • この記事いいね! (0)
著者:杉浦

dusk中でwebdriverを呼び出し多彩な操作をする

 Laravelのブラウザテストツールduskは以下の様なコードでdriverメソッドからwebdriverを呼び出すことによってブラウザを動かします。このコードはLaravelのデフォルトに近い形でGoogle Chromeを呼び出しています。

    /**
     * Create the RemoteWebDriver instance.
     * @return \Facebook\WebDriver\Remote\RemoteWebDriver
     */
    protected function driver()
    {
        $options = (new ChromeOptions())->addArguments([
            '--disable-gpu',
            '--headless',
        ]);

        return RemoteWebDriver::create(
            'http://localhost:9515',
            DesiredCapabilities::chrome()->setCapability(ChromeOptions::CAPABILITY, $options),
            300 * 1000,
            300 * 1000
        );
    }

 通常duskはLaravel\Dusk\Browserクラスからbrowserを操作します。Browserクラスは堅牢であり、クラス中に生やされたプロパティによるブラウザの状態の把握は便利です。Browserクラスは便利ですが、操作に難があります。本当に手打ちの操作を再現するのみならば問題がない程度には操作メソッドが備わってますが、これは手打ちの再現であり手打ちでは膨大な時間がかかる入力をテストする際に面倒が起きます。typeメソッドによる長文入力はこれに見事に引っかかります。
 BrowserクラスはRemoteWebDriverクラスをラッピングしてブラウザ操作を行っています。Browserクラスの下に入っているRemoteWebDriverクラスを直接操作することでより多彩な操作ができます。呼び出し方はいたって簡単次のコードの通り

/** @var Browser $browser */
$web_driver = $browser->driver;

 この$web_driver変数からRemoteWebDriverクラスのメソッドを呼びだすことによってさまざまなことが出来ます。できることの一覧はFacebook\WebDriver\Remote\RemoteWebDriver | php-webdriver APIやvendor/facebook/webdriver/lib/Remote/RemoteWebDriver.phpの中身にあります
 特に便利なメソッドはexecuteScriptとtakeScreenshotです。executeScriptは次の様にjavascriptを実行できます。長文入力の様な手打ちでやっていられない、手打ちの必要が無い操作はこれで肩代わりするによって実行時間の短縮が図れます。

$browser->driver->executeScript('document.querySelector("[name=\"' . $field . '\"]").value = "' . $value . '";');

 takeScreenshotはその名の通りスクリーンショットを取得するメソッドです。Laravelのヘルパ関数によるpath指定を行うことでLaravelプロジェクト内部に任意のタイミングで取得したスクリーンショットを保存できます。

  • この記事いいね! (0)
著者:杉浦

LaravelのCRUDテストとfactory

 CRUDの内updateとdeleteは既存データを加工します。この機能をテストするならば、必然データを壊します。この壊すデータの範囲に重要なものが紛れている場合、問題が起こります。私は今日テスト環境の画面確認用管理者アカウントを壊しました。
 update,deleteをテストするために重要なデータをテスト対象とした場合、テスト中止ないし再起動とする方法があります。この方法にはテスト対象とした範囲に重要なデータしか存在しない場合テストを行えないという欠点があります。もう一つ新たにデータを作成し、それに対してupdate,deleteをテストする方法があります。この手法はコード量が増えがちです。Laravelはfactoryという仕組みを用いてコード量の節約、責任の分担をしています。
データベースのテスト 5.6 Laravel#ファクトリの記述
 ファクトリはダミーデータを作成する仕組みでデータベースにテスト用初期データを入れる、データベースのテストに用いるといった使い方が主ですが、更新、削除のテストでもよく働いてくれます。factoryは次の様な記述によって与えるデータを定義します。

/** /database/factories/UserFactory.php */
use Faker\Generator as Faker;

$factory->define(App\User::class, function (Faker $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->unique()->safeEmail,
        'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret
        'remember_token' => str_random(10),
    ];
});

 これを定義した場合、次の様にダミーデータをワンライナーで呼び出せます。createはデータベースへの書き込みを実行してインスタンスのCollectionを返し、makeはインスタンスのCollecitonを返します。

// factory(Model::class, 個数)
$user = factory(User::class, 1)->create()->first();
$new_user_data = factory(User::class, 1)->make()->first();

 createは削除と更新用の壊していいデータ、makeは更新用の入力値として便利に使えます。

  • この記事いいね! (0)