20191124のPHPに関する記事は18件です。

PHPがメインの経験者が、Pythonを始めて5日目の話(selenium)PHP vs Python

はじめに

今回はseleniumを使用してChromeブラウザをコントロールしてWEBのサービスに保存されているファイルをダウンロードするのが目的です。もう一点の重要な目的はPHPとPythonで同じダウンロードを行い、結果の比較をしたいと思います。ダウンロードはWEBメールの添付ファイルが最初の目的でしたが、Slack や Facebook Cybozu 等、現在はPHPのコードで大体可能です。この話を聞いた某編集者がハッキングだと言われましたが、普通に自分でログインしてダウンロードするのと基本的に変わりませんので、堂々とやって良い処理です。以下、PHPだと selenium-webdriverを利用する事になる訳で、今になりPythonの場合と比較して複雑に感じます。PHPで動かす場合には次の記事を参考にさせていただきました。この記事にあるように準備が色々とあります。「seleniumを使ってPHPでChromeの自動操作をする」その準備を終えて、seleniumを起動します。

1.PHP編(seleniumの起動)
java -jar selenium-server-standalone.jar &
2.PHP編(実際にログインしダウンロードまで行きます)

このコード、一部意図的では無い部分があります。結果的に試行錯誤して上手くログインしてダウンロード出来たので採用したコードで、例えばスクリーンショットも不要ですが、撮るとダウンロード出来たのでそのまま残したものです。PHPでseleniumは以外に大変なのかもしれません。これからPythonで同じ事をやるとどうなるか楽しみですが、Pythonの場合もseleniumのインストールを別途やらなくてはならないので、同じ環境でやって良いものか気になります。

download.php
require_once './vendor/autoload.php';

use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\WebDriverExpectedCondition;
use Facebook\WebDriver\WebDriverBy;

// ダウンロードしたchromedriverのパスを指定
$screenPath = $relative.'/g_screenshot.png';
$driverPath = '/usr/local/bin/chromedriver';
putenv("webdriver.chrome.driver=" . $driverPath);
// Chromeを起動するときのオプション指定用
$options = new ChromeOptions();
// ヘッドレスで起動するように指定
$options->addArguments([
'--no-sandbox',
'--headless',    // ヘッドレスで起動するように指定。ダウンロードフォルダ指定が無効になる。
'--disable-gpu',    // ヘッドレスで暫定的に必要なフラグ
'--ignore-certificate-errors',  // SSLセキュリティ証明書のエラーページ(「このサイトのセキュリティ証明書は信頼できません」のページ)を表示しません。
]);
$caps = DesiredCapabilities::chrome();
$caps->setCapability(ChromeOptions::CAPABILITY, $options);
$driver = ChromeDriver::start($caps, null, 1000*60*5, 1000*60*10);
$path = dirname(__FILE__).'/data'; # ダウンロードされるWEBサーバーのパス(このプログラムからのパス)
$this->setDownloadDir($driver, $path);
$driver->manage()->window()->maximize();

// 仮想ログイン、$atarget はダウンロードするファイルのリンク
$driver->get($wtarget); # このリンク先のファイルがダウンロードされます。
$element = $driver->findElement(WebDriverBy::name('username'));
$element->sendKeys($wuser);
$element = $driver->findElement(WebDriverBy::name('password'));
$element->sendKeys($wpass);
$element->submit();
$driver->manage()->timeouts()->implicitlyWait(5);
$driver->takeScreenshot($screenPath);
//$driver->manage()->getCookies();
// 仮想ログイン完了
3.PHPで構築した環境で from selenium import webdriver が動作しない

この問題ですが、
Python 3.7.5 (default, Nov 1 2019, 19:15:52) では未だにエラーになっているのですが、もう一つの
Python 2.7.17 (default, Oct 25 2019, 10:08:31) ではすんなり動きます。二つの環境がそれぞれ呼び出すモジュールの関係なのでしょう。

webdriver.py
# 限りなく自分用のメモです。
from selenium import webdriver # from <module> import <driver>
# ImportError: cannot import name 'webdriver' from 'selenium' (unknown location)

ほぼパスの問題だと思いますが、Python 3.7.5 はエラーのままになっています。

4.google-chrome と chromedriver のバージョンの問題かもしれない

selenium をPHPで動かし出した時期にPythoのサンプルでテストをしていたのですが、その時期には動いていたPythonのサンプルがwebdriverを読み込んだ時点で止まります。そのエラーは google-chrome のバージョンと chromedriver のバージョンを示していて、クラッシュしている雰囲気なのです。PHPについては現在実装されている google-chrome と chromedriver を変えると逆に google-chrome がクラッシュします。この関係は何か理由がありそうなのです。少々悩みます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPでプログラム全体の設定に使う変数の保持の仕方

PHPでちょっとしたアプリを作ったりちょっとしたフレームワークを作ったりするとき、共通の全体設定をどのように書くかという悩みがある。
頭の中を整理するためにパターン分けしてみる。説明は独断と偏見に基づいている、かもしれない。

定数

定数にはdefineconstがある。

<?php
define('FOO', 'foo');
const BAR = 'bar';

echo FOO;
echo BAR;

define

みんな大好きdefine。古くから利用されている。
単純で分かりやすく、関数が使えるのが最大のメリット。

WordPressでは

wp-config.php
define('DB_NAME', 'wordpress');
define('DB_USER', 'wordpress');
define('DB_PASSWORD', 'secret');
define('DB_HOST', 'localhost');

こんな感じで利用されている。
データベースの変数やプログラムのPATH設定などで主に利用される。

CakePHP では

config/paths.php
define('ROOT', dirname(__DIR__));
define('APP_DIR', 'src');
define('APP', ROOT . DS . APP_DIR . DS);

このようにPATHが設定されている。

何らかの条件にしたがって定数定義を切り替える場合にも利用される。

if ($_SERVER['SERVER_NAME'] == 'localhost'){
  define('ENV', 'development');
  define('DEBUG', true);
}else{
  define('ENV', 'production');
  define('DEBUG', false);
}

また、definedで定義済みかどうかを調べることができる。

if (defined('DEBUG')){
  define('ENV', 'development');
}else{
  define('ENV', 'production');
}

プログラムを配布して設定ファイルを修正、FTPでアップ、ぐらいのツールで使うには便利である。

デメリットとしては、グローバル空間が汚染されるので数が多くなってくると管理しきれないという問題がある。また、複数のライブラリで同じ定数が使われていると競合を起こす。

例えばEC-CUBE2には

data/config/config.php
define('DB_USER', 'eccube');
define('DB_PASSWORD', 'eccube');
define('DB_SERVER', 'localhost');
define('DB_NAME', 'eccube');

こういったDB設定があり、WordPressとEC-CUBEを両方読み込もうとするとエラーになる。

一応、namespaceも使うことができる。

config.php
define('App\Controller\Index\PAGE_ENV', 'page env');
src/controller/Index.php
namespace App\Controller;
class Index {
    public static function index(){
        echo Index\PAGE_ENV, "\n";
    }
}

可能というだけで、このような使い方は見かけない。おそらく下記constを使うのであろう。

const

クラス定数としてconstが利用できる。
トップレベルのグローバルに

config.php
const DEBUG = true;

のようにも書けるが、主には

Psr/Log/LogLevel.php
namespace Psr\Log;
class LogLevel
{
    const EMERGENCY = 'emergency';
    const ALERT = 'alert';
    const CRITICAL = 'critical';
    const ERROR = 'error';
    const WARNING = 'warning';
    const NOTICE = 'notice';
    const INFO = 'info';
    const DEBUG = 'debug';
}

このようにnamespaceclassconstのセットで利用される。
トップレベルやnamespace直下だとオートロードできないので、手でrequire_onceを書かないといけないのであまり嬉しくない。

今では演算子が使えるので単純な数値計算や文字列連結はできる。

class Category {
    const CATEGORY1 = 1;
    const CATEGORY2 = 2;
    const SPECIAL_CATEGORY = 999;
    const CATEGORIES = [
        self::CATEGORY1 => 'category 1',
        self::CATEGORY2 => 'category 2',
    ];
    const SPECIAL_CATEGORIES = [
        self::SPECIAL_CATEGORY => 'special',
    ];
    const ALL_CATEGORIES = self::CATEGORIES + self::SPECIAL_CATEGORIES;
}
var_dump(Category::ALL_CATEGORIES);

/*
array(3) {
  [1] =>
  string(10) "category 1"
  [2] =>
  string(10) "category 2"
  [999] =>
  string(7) "special"
}
*/

なので多少凝ったこともできる。

アプリケーションの設定として利用することはあまり無いと思われる。大抵はそのクラスにおける固定値を定義するために使う。

constは(直接は)関数呼び出しができない。

これを書いていて今気付いたのだが、どうしてもconstで関数呼び出しをしたい場合は少しトリッキーなことをすれば出来た。

Mode.php
function get_mode() {
    $host = $_SERVER['SERVER_NAME'] ?? '';
    if ($host === 'example.com')
        return 'staging';

    if (preg_match('/^[[:alnum:]]\.example\.com$/', $host))
        return 'staging';

    return 'development';
}
define('GET_MODE', get_mode());

class Mode {
    const APP_MODE = GET_MODE;
}
var_dump(Mode::APP_MODE);

defineは関数呼び出しできるので、constからdefineを経由して関数を呼び出せる。使い道は不明。

設定ファイル読み込み

XMLJSONのような特別なフォーマットの設定ファイルを用意して、それを読み込む方法もある。
また、PHPをそのまま設定ファイルのように使う方法もある。

return array

Laravel や FuelPHP、CakePHPなど主要なフレームワークはこの方式を採用しているようだ。

例えばLaravelのデータベース設定では

config/database.php
return [
    'default' => env('DB_CONNECTION', 'mysql'),
    'connections' => [

        'sqlite' => [
            'driver' => 'sqlite',
            'database' => env('DB_DATABASE', database_path('database.sqlite')),
            'prefix' => '',
        ],
    .....
];

このようになっている。
利用する側は

$config = include 'config/database.php';

とすれば、$configreturnした値が入る。
関数も使えるしグローバルも汚染しない。シンプルでもある。ファイルを解析する必要がないので速度も速い。良いことずくめである。

敢えてデメリットを言うとすれば、このファイルがPHPなので、文法を間違えばアプリケーションが停止するしreturn [の前に自由にPHPのプログラムを書けてしまうということだろうか。

普通の変数

限定スコープ

PHPのincludeは呼び出し側のスコープに影響される。設定ファイルのグローバル空間に変数を定義したとしても、読み込む側が関数の中でincludeしていればグローバルが汚染されることはない。

CodeIgniter でのデータベース設定ファイルは

application/config/database.php
<?php
$active_group = 'default';
$query_builder = TRUE;
$db['default'] = array(
    'dsn'   => '',
    'hostname' => 'localhost',
    'username' => 'username',
    'password' => 'password',
    'database' => 'codeigniter',
   ...
);

このようになっていて、普通にグローバル空間に変数を定義している。

呼び出し側で

system/database/DB.php
function &DB($params = '', $query_builder_override = NULL)
{
  ...
  // (編集済み)
  $file_path = APPPATH.'config/database.php';
  include($file_path);

  ...
}

このように関数の中でincludeすると、$db$query_builderはグローバルスコープではなく関数のローカルスコープとして読み込まれる。
使い方によっては便利で、テンプレートエンジンの変数設定時にはよく使われる手法だが、気を付けないと関数の中の変数が上書きされる危険もある。
全てのローカル変数とinclude時の変数展開の動作を完全に把握している場合にのみ使うべき。

グローバル変数

WordPressではグローバル変数が普通に使われる。
関数もグローバルだし、グローバル関数を呼び出したら内部状態が変更されたりする。
ある意味PHPらしい構造と言える。

function my_func(){
    global $wpdb;
    ....
}

というような冒頭にglobal宣言するような記述はWordPressではよく見かける。

ただし自分でこのような設計をするのはかなり難しい。
全部グローバルに書いて無駄に複雑になったプログラムを時々見かけた。

気を付けていれば問題ないPHP の究極系がWordPressかもしれない。

特定のフォーマットのファイル

yaml

PHP以外の言語ではよく利用されるが、PHPではあまり見かけない。組み込み関数でパースできないからだろうか。
フレームワークのSymfonyの設定で使われているので、もしYAMLを使う場合は

composer require symfony/yaml

Symfonyのパッケージを使うのが良いと思う。

json

composerの設定がjsonなのでPHPで使われているといえば使われている。
組み込み関数のjson_decodeですぐPHPの配列に変換できるのも良い。
でもアプリケーションの設定としてはあまり見かけない。異なるプログラミング言語間のデータ通信フォーマットとしてのイメージが強いからだろうか。

XML

PHPUnitなどJava由来のものに使われている。
書くのがめんどくさい。解析もめんどくさい。

DTDをがっつり定義する大規模なシステムでは使われている…?あまり知らない。
DOM (https://php.net/dom) も XML (https://php.net/xml) も組み込み関数で使える。

ini

サーバー設定ファイルなどで使われているiniファイル。
個人的には好きなのだが、まったく使われていない。

config.ini
[core]
di = "My\Container"
env = development

[app]
name = "My Application"
data[] = data1
data[] = data2
hash[app1] = app1
hash[app2] = app2

[config]
debug = false
staging = true
error = E_ALL ^ E_DEPRECATED
config.php
$config = parse_ini_file('config.ini', true);
var_dump($config);

/*
array(3) {
  'core' =>
  array(2) {
    'di' =>
    string(12) "My\Container"
    'env' =>
    string(11) "development"
  }
  'app' =>
  array(3) {
    'name' =>
    string(14) "My Application"
    'data' =>
    array(2) {
      [0] =>
      string(5) "data1"
      [1] =>
      string(5) "data2"
    }
    'hash' =>
    array(2) {
      'app1' =>
      string(4) "app1"
      'app2' =>
      string(4) "app2"
    }
  }
  'config' =>
  array(3) {
    'debug' =>
    string(0) ""
    'staging' =>
    string(1) "1"
    'error' =>
    string(5) "24575"
  }
}
*/

定数を展開するとか演算が出来てしまうなどの妙なハマりどころがあるからだろうか。
php.iniの書き方がそのまま使えるので、ある意味便利、ある意味おせっかい。

クラス

アプリケーション設定と言っていいのか分からないが、クラスにしてしまえば何でもできる。
グローバル変数的に使うのでSingletonにする。

<?php
namespace App;
class Config
{
    public $APP_ENV = 'development';
    private function __construct(){}
    public static function getInstance(){
        static $instance;
        if ($instance === null){
            $instance = new self();
        }
        return $instance;
    }
}

より細かく制御したいなら

namespace App;
class Config
{
    public static function factory(){
        switch ($name = getenv('APP_ENV')){
        case 'production':
        case 'development':
        case 'testing':
            $cls = 'App\Config\\' . ucfirst($name);
            return new $cls;
            break;

        default:
            return new App\Config\Development();
            break;
        }
    }
}

Factoryにして設定クラスを分けつつ、Auto Wiring時にシングルトンとしてコンテナに登録するのが普通だろうか。

実際のフレームワークでは、こういった設定用クラスからdefineconstreturn arrayの各設定ファイルが呼び出されることになる。

まとめ

書いてみたけど、どれかを選ぶのはなかなか難しい。
エディタで定義にすぐジャンプできるかという視点もある。この視点だとdefineまたはconstが強い。

設定が大量にある処理はYAMLかreturn array、PATHのように各所で使うけど単純なものはdefine、環境別API設定のような複雑なものはクラス、というようにそれぞれ別々の方式を併用するのがPHPらしいのかもしれない。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelでクロージャを使った自作バリデーションの作成

Laravelのバリデーションでチェックボックスの特定の組み合わせを不可にするために、
自作バリデーションを作成してみました。
自作バリデーションはRulesを作る方法がありますが、
調べた所、バリデーションでもクロージャが使えるようでした。

簡単なサンプルを作ってみました。
まずフォームを作ります。

<h1>果物を選んで下さい。</h1>
@if ($errors->any())
    <ul>
        @foreach ($errors->all() as $error)
            <li>{{ $error }}</li>
        @endforeach
    </ul>
@endif
<form action="{{ route('check') }}" method="POST">
    @csrf
    <input type="checkbox" name="fruits[]" value="apple">りんご
    <input type="checkbox" name="fruits[]" value="orange">みかん
    <input type="checkbox" name="fruits[]" value="banana">バナナ
    <input type="submit">
</form>

りんごとバナナに同時にチェックがある時にエラーにするために、
コントローラーに以下のバリデーションを追加します。
fruitsのバリデーションの1つとしてクロージャを組み込んでます。
$attributeにフォーム名、$valueにフォームの値が入り、
$failにエラーメッセージ入れて返すという形になります。

public function check(Request $request)
{
    $request->validate([
        'fruits' => [
            'required',
            function ($attribute, $value, $fail) {
                if (in_array('apple', $value) && in_array('banana', $value)) {
                    return $fail('りんごとバナナは同時に選べません。');
                }
            }
        ],
    ]);
    return view('input');
}

少し可読性は悪いですが、別クラスを作らなくてすむのは便利です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Twigで拡張子が2つある理由

Twigのファイル名は、「index.html.twig」と「html」+「twig」と拡張子が2つついています。

これは「twig」というのが、Twigのテンプレートファイルということを示し、「html」は、twigファイルをレンダリングして生成されるものが、「HTML」ファイルであることを示している。

これは、Template Namingに記載されていました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

セッション有効期限の伸ばし方

セッション有効期限の伸ばし方

自分の学習用ログ

セッションの有効期限を30日間にする場合

//1、セッションファイルの置き場を変更する
session_save_path("var/tmp");

// 2、ガーベージコレクションが削除するセッションの有効期限を設定
ini_set('session.gc_maxlifetime',60*60*24*30);

// 3、ブラウザを閉じても削除されないようにクッキー自体の有効期限を伸ばす
ini_set('session.cookie_lifetime',60*60*24*30);

// 4、セッションを使う
session_start();

// 現在のセッションIDを新しく生成したものと置き換える
session_regenerate_id();
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelで日付をYYYY年MM月DD日 (曜日) hh: mmと表示させる

日付と時間だけだと簡単ですが、間に曜日を挟んだ場合の情報が少なくて面倒だったので、メモ
LaravelにCarbonのv2以降をインストールすれば以下のように1行でかけます。
今回は参考にdirectiveを使ってますが、viewHelperとかの方がいいかも。

AppServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        \Blade::directive('datetime_and_week', function ($expression) {
            return "<?php echo Carbon\Carbon::parse($expression)->isoFormat('YYYY年MM月DD日 (ddd) LT '); ?>";
        });
    }
}

directiveなのでView clearコマンドを実行後に
viewなどで下記のように呼び出せばOK

Sample.blade.php
<?php

@datetime_and_week('2019-11-24 00:00:00')

下記のように表示されます!
2019年11月24日 (日) 00:00

他に良い方法あれば教えてください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelのファサードについて簡単にまとめる

概要

Laravelの代表的な機能としてファサードがある。これを使うと、staticなメソッドを呼ぶのと同じ要領で、どこからでも簡単にフレームワークの機能を呼び出すことができる。

use Illuminate\Support\Facades\Cache;

Route::get('/cache', function () {
    return Cache::get('key');
});

下の例のように、useで名前空間を指定せずとも、グローバル名前空間でアクセスするように書くことも可能。(ちなみにこれが可能なのはconfig/app.phpaliasesに記述されている対応関係をclass_alias関数によって結びつけているから。要はエイリアス(別名)をつけて省略的に書けるようにしていると思っておけば良いと思う。たぶん。)

Route::get('/config', function () {
    return \Config::get('key');
});

これを一見すると、Illuminate\Support\Facades\Configクラスに実装されたgetメソッドを呼んでいるように見えるが、実はgetメソッドはIlluminate\Support\Facades\Configクラスにも、継承元のIlluminate\Support\Facades\Facadeクラスにも存在していない。実際には、getFacadeAccessor()が実装されているのみである。

Illuminate\Support\Facades\Config.php
<?php

namespace Illuminate\Support\Facades;

class Config extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'config';
    }
}

この時何が起こっているか、簡単に辿ってみる。

マジックメソッドと__callStatic()

Illuminate\Support\Facades\Configクラスはget()を持っていなかった。
実装されていないstaticなメソッドを呼ぼうとするとどうなるかというと、PHPでは__callStatic()というマジックメソッドが呼ばれることになる。

public static __callStatic ( string $name , array $arguments ) : mixed

__callStatic() は、 アクセス不能メソッドを静的コンテキストで実行したときに起動します。
引数 $name は、 コールしようとしたメソッドの名前です。 引数 $argumentsは配列で、メソッド $name に渡そうとしたパラメータが格納されます。

マジックメソッドはPHPにおける特殊な関数であり、開発者が直接指定して呼び出すことは基本的になく、特定のタイミングで勝手に呼び出される。代表的なものが馴染み深い__construct()であり、新たにインスタンスが生成されるタイミングで勝手に呼び出されるマジックメソッドといえる。

中身

継承元のIlluminate\Support\Facades\Facadeクラスには、__callStatic()が実装されている。(コメントなどは省略)

Illuminate\Support\Facades\Facade.php
<?php

namespace Illuminate\Support\Facades;

...

abstract class Facade
{
    ...

    public static function getFacadeRoot()
    {
        return static::resolveFacadeInstance(static::getFacadeAccessor());
    }

    protected static function resolveFacadeInstance($name)
    {
        if (is_object($name)) {
            return $name;
        }

        if (isset(static::$resolvedInstance[$name])) {
            return static::$resolvedInstance[$name];
        }

        if (static::$app) {
            return static::$resolvedInstance[$name] = static::$app[$name];
        }
    }

    public static function __callStatic($method, $args)
    {
        $instance = static::getFacadeRoot();

        if (! $instance) {
            throw new RuntimeException('A facade root has not been set.');
        }

        return $instance->$method(...$args);
    }
}

上記のPHPマニュアルからの引用の通り、__callStatic()の引数$methodには呼び出そうとしたメソッドの名前、$argsにはその時に使用した引数が配列の形式で入る。

getFacadeRoot()でサービスコンテナ($app)から対象となるインスタンスを取得し、そのインスタンスのメソッドを引数の$methodによって実行する。

つまり、staticなメソッドを使用しているように見えて、実際には実体となるインスタンスがサービスコンテナから取り出され、指定したメソッドが実行される、という流れになっていることがわかる。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ファサードの内部における動作を簡単にまとめる【Laravel】

概要

Laravelの代表的な機能としてファサードがある。これを使うと、staticなメソッドを呼ぶのと同じ要領で、どこからでも簡単にフレームワークの機能を呼び出すことができる。

use Illuminate\Support\Facades\Cache;

Route::get('/cache', function () {
    return Cache::get('key');
});

下の例のように、useで名前空間を指定せずとも、グローバル名前空間でアクセスするように書くことも可能。(ちなみにこれが可能なのはconfig/app.phpaliasesに記述されている対応関係をclass_alias関数によって結びつけているから。要はエイリアス(別名)をつけて省略的に書けるようにしていると思っておけば良いと思う。たぶん。)

Route::get('/config', function () {
    return \Config::get('key');
});

これを一見すると、Illuminate\Support\Facades\Configクラスに実装されたgetメソッドを呼んでいるように見えるが、実はgetメソッドはIlluminate\Support\Facades\Configクラスにも、継承元のIlluminate\Support\Facades\Facadeクラスにも存在していない。実際には、getFacadeAccessor()が実装されているのみである。

Config.php
<?php

namespace Illuminate\Support\Facades;

class Config extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'config';
    }
}

この時何が起こっているか、簡単に辿ってみる。

マジックメソッドと__callStatic()

Illuminate\Support\Facades\Configクラスはget()を持っていなかった。
実装されていないstaticなメソッドを呼ぼうとするとどうなるかというと、PHPでは__callStatic()というマジックメソッドが呼ばれることになる。

public static __callStatic ( string $name , array $arguments ) : mixed

__callStatic() は、 アクセス不能メソッドを静的コンテキストで実行したときに起動します。
引数 $name は、 コールしようとしたメソッドの名前です。 引数 $argumentsは配列で、メソッド $name に渡そうとしたパラメータが格納されます。

マジックメソッドはPHPにおける特殊な関数であり、開発者が直接指定して呼び出すことは基本的になく、特定のタイミングで勝手に呼び出される。代表的なものが馴染み深い__construct()であり、新たにインスタンスが生成されるタイミングで勝手に呼び出されるマジックメソッドといえる。

中身

継承元のIlluminate\Support\Facades\Facadeクラスには、__callStatic()が実装されている。(コメントなどは省略)

Facade.php
<?php

namespace Illuminate\Support\Facades;

...

abstract class Facade
{
    ...

    public static function getFacadeRoot()
    {
        return static::resolveFacadeInstance(static::getFacadeAccessor());
    }

    protected static function resolveFacadeInstance($name)
    {
        if (is_object($name)) {
            return $name;
        }

        if (isset(static::$resolvedInstance[$name])) {
            return static::$resolvedInstance[$name];
        }

        if (static::$app) {
            return static::$resolvedInstance[$name] = static::$app[$name];
        }
    }

    public static function __callStatic($method, $args)
    {
        $instance = static::getFacadeRoot();

        if (! $instance) {
            throw new RuntimeException('A facade root has not been set.');
        }

        return $instance->$method(...$args);
    }
}

上記のPHPマニュアルからの引用の通り、__callStatic()の引数$methodには呼び出そうとしたメソッドの名前、$argsにはその時に使用した引数が配列の形式で入る。

getFacadeRoot()でサービスコンテナ($app)から対象となるインスタンスを取得し、そのインスタンスのメソッドを引数の$methodによって実行する。

つまり、staticなメソッドを使用しているように見えて、実際には実体となるインスタンスがサービスコンテナから取り出され、指定したメソッドが実行される、という流れになっていることがわかる。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[AWS SDK for PHP]署名付きPOSTを使い、直接クライアント側からAWS S3にファイルアップロード(PostObjectV4インスタンスを使用)

以下の流れで実装しました。

  • PostObjectV4 のインスタンスを使い、署名付きPOSTに必要な情報を発行
  • Vue.jsで、アプリケーションサーバを介さず、クライアント側から直接S3にファイルをアップロード

画面はこんな感じで、簡単にアップロード画面を作ってます。
スクリーンショット 2019-11-18 23.51.42.png

準備

AWS

S3でバケットを作成してください。
※IAMの設定は省略します

S3 CORSの設定

CORSを設定しておかなければなりません。

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

AWS-SDK-PHPのインストール

$ composer require aws/aws-sdk-php

実装

署名付きPOSTに必要なデータを発行するPHPの処理と、ファイルアップロードするVue.jsの処理を記載します。
実装した処理は以下の通りです。

  • 署名付きPOSTに必要なデータ形式のjsonを返すAPI
  • Vue.jsから↑のAPIを叩き、レスポンスを取得
  • レスポンスを利用し、ファイルアップロード

API(PHP)

<?php
    // laravel apiのcontrollerの処理を一部抜粋
    public function getPresignedUrl(Request $request)
    {
        $s3Client = new \Aws\S3\S3Client([
            'credentials' => [
                'key' => env('AWS_ACCESS_KEY_ID'),
                'secret' => env('AWS_SECRET_ACCESS_KEY'),
            ],
            'region' => env('AWS_DEFAULT_REGION'),
            'version' => 'latest'
        ]);

        $bucket = env('AWS_BUCKET');
        $requestData = $request->all();
        $formInputs = [
            'acl' => 'public-read',
            'key' => 'hoge/' . $requestData['filename'] . '.' . $requestData['fileext'],
        ];
        $options = [
            ['acl' => 'public-read'],
            ['bucket' => $bucket],
            ['starts-with', '$key', 'hoge/'],
        ];
        $expires = '+20 minutes';
        $postObject = new \Aws\S3\PostObjectV4(
            $s3Client,
            $bucket,
            $formInputs,
            $options,
            $expires
        );

        $formAttributes = $postObject->getFormAttributes();
        $formInputs = $postObject->getFormInputs();

        return response()
            ->json([
                'url' => $formAttributes['action'],
                'fields' => $formInputs
            ]);
    }

Client側(Vue.js)

<script>
    export default {
        name: 'AwsS3Upload',
        methods: {
            async upload() { 
                const upload_files = document.getElementById('upload-file');
                const upload_file = upload_files.files[0];
                // 署名付きPOSTのAPI叩く
                let preSignedUrl = await this.getPresignedUrl();
                // S3へアップロード
                let uploadS3Path = await this.uploadS3(preSignedUrl, upload_file);
            },
            async getPresignedUrl() {
                // ↓ここのファイル名は仮置きで適当になってますw
                let filename = 'fuga';
                let filetype = 'image/jpeg'
                let fileext = 'jpg'
                try {
                    const url = '/api/get-presigned-url?filename=' +  filename + '&filetype=' + filetype + '&fileext=' + fileext;
                    let response = await axios.get(url);
                    console.log('S3署名付きURL取得 成功');

                    return response;
                } catch (error) {
                    console.log('S3 署名付きURL取得 失敗');
                }
            },
            async uploadS3(presignedUrl, up_file) {
                let data = presignedUrl.data;
                try {
                    var formdata = new FormData();
                    for (let key in data.fields) {
                        formdata.append(key, data.fields[key]);
                    }
                    formdata.append("file", up_file);
                    const headers = {
                        "content-type": "multipart/form-data",
                    }
                    console.log('S3 アップロード 開始');
                    let response = await axios.post(
                        data.url,
                        formdata,
                        {
                            headers: headers,
                        }
                    );
                    console.log('S3 アップロード 成功');
                    return data.url + '/' + data.fields.key;
                } catch (error) {
                    console.log('S3 アップロード エラー');
                }
            },
        }
    }
</script>

結論

署名付きPOSTで、S3へ直接クライアントサイドからファイルアップロードができました。

参考

【AWS S3】S3 Presigned URLの仕組みを調べてみた
CORS(Cross-Origin Resource Sharing)について整理してみた
PresignedPost.php
ブラウザからS3へのダイレクトアップロード

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP7.4のpreloadいれたらLaravelは早くなるのだろうかと思って検証した

皆さんこんにちは

PHP7.4でpreloadが来るっていうので、ワクテカしているわけですが、実際どれくらい早くなるのかしらって思いまして、ちょうど色々検証していたので、Laravelでも軽くやっちゃおうぜって思ったので、やってみます。

あと4日ですね!!

三行で

  • preloadが来るよ
  • preloadで読み込み済みの状態を作れるよ
  • Laravelに導入すると、結構早くなるよ

Preload

PHP7.4でopcacheにpreloadという機能が入ります。これの概要は

  • opcacheの設定にpreloadを実行するヘルパーファイルを指定できる
  • webサーバを起動するとopcacheがヘルパーファイルを実行する
  • ヘルパーファイル内でキャッシュ化したものは、通常のアクセスにおいて、すでに読み込まれたものとしてみなされる

そう、読み込み済みですよ!
簡単に読み込み済みの効果を見るために、以下のような例を考えてみましょう。

preload利用の例

まずは、こんなDockerfileで、動かせるコンテナを作ってみます。

FROM php:7.4.0RC4-cli

RUN apt update && apt install -y vim sudo && docker-php-ext-install opcache &&\
    echo 'opcache.preload=/var/www/preload.php' >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini &&\
    useradd nginx

WORKDIR /var/www

ベースコンテナはちょいと古いのですが、まあ、検証当時のものなんで、許してくださいな。
そして、以下のようなディレクトリ構成のファイル群を用意します。

classes
 - A.php
 - B.php
index.php
preload.php

内容としては

A.php
<?php 
class A
{
    public function name(): string
    {
        return return static::class;
    }
}
B.php
<?php
class B extends A {}
index.php
<?php
$b = new B;

echo $b->name() . '<BR>';

さて、サーバのエンドポイントとしてはindex.phpを使うわけですが、この中ではrequireを一回も使っていないし、当然autoloadの設定もしていないので、

sudo -u nginx php -S 0.0.0.0:8080

としてサーバを起動しても、いざアクセスすると「Bなんてクラス知らんのだけど」っていうエラーが出るだけです。

ここで、preloadを使ってみましょう。
先程のDockerfileには予めopcache.preloadの設定が挿入されるようになっています。実際に書かれている設定はこんな感じになっています。

/usr/local/etc/php/conf.d/docker-php-ext-opcache.ini
zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20190902/opcache.so
opcache.preload=/var/www/preload.php

このopcache.preloadがヘルパーファイルの場所になっているので、そこに以下のようなヘルパーをおいておきましょう。

preload.php
<?php

$files = glob(__DIR__ . '/classes/**.php');

foreach ($files as $file) {
    opcache_compile_file($file);
}

この状態でサーバを起動します。

sudo -u nginx php -S 0.0.0.0:8080
[Sun Nov 24 04:54:36 2019] PHP 7.4.0RC4 Development Server (http://0.0.0.0:8080) started

ちゃんと起動したようです。
それではアクセスしてみましょう。

$ curl http://localhost:8080
B<BR>

普通に動きますね。

preloadは性能に絡めるのか

多くのWebフレームワークは大量のクラスが定義されていますが、PHPでそれを読むにはいちいちファイルを見に行かなければなりません。しかも、PHPでは、基本的に各アクセスごとにプロセスが独立しているため、ファイルの読み込みもアクセスごとに発生します。
opcacheでこの動きを緩和することができますが、その動きは割と複雑です。

  1. クラスが呼ばれる (静的・インスタンス生成など)
  2. クラスの定義がない場合、autoloadで設定したルールに従い、ファイルをrequireする
  3. requireしようとしたファイルのキャッシュがあった場合、ファイルのタイムスタンプよりも新しければ、そのままキャッシュを使う
  4. キャッシュが古ければ、ファイルを読みに行く(設定によってスキップ可能)
  5. ファイルを読んだ場合はキャッシュを更新する

preloadしてあれば、1ですむので、それなりに早くなるんじゃないかって思います。

Laravelに入れてみる

たくさんのファイルを読み込むといえばLaravelですね!
そこで、適当にLaravelを入れて、検証してみましょう。

includeしているファイルを見てみる

とりあえず、どのくらいのファイルを読み込んでいるのか見てみます。

public/index.php
$kernel->terminate($request, $response);

$includes = get_included_files();

file_put_contents('/var/www/includes.txt', implode("\n", $includes));

こんな感じのを仕込むと、ファイルのリストが出てきます。350ファイル以上ありますね。

preloadに入れてみる

適当にpreloadに入れるわけですが、viewのキャッシュファイルとかは読んでもしょうがないかなとか思いながら、以下のようなloaderを作ります。

preload.php
<?php
$includes = file_get_contents('/var/www/includes.txt');
$includes = explode("\n", $includes);

foreach ($includes as $include) {
    if (strpos($include, '/storage/framework') !== false) {
        echo $include, " - storage \n";
        continue;
    }

    if (strpos($include, 'index.php') !== false) {
        echo $include, " - index \n";
        continue;
    }

    if (strpos($include, 'src/config') !== false) {
        echo $include, " - config\n";
        continue;
    }

    opcache_compile_file($include);
}

こいつをpreloadした状態でサーバを起動します。
index.phpにerror_log(count($includes));みたいなコードを突っ込んでやると、読み込みファイル数が363 -> 46 になりました。せっかくなら1とかになってくれるとありがたかったんですが、そううまくは行かないようですね。

検証する

例によってab使って一分間のアクセスレートを見てみましょう。
abはubuntu 18.04 (wsl2のやつ) を使ってます。

preloadなし

$ ab -t 60 -c 1 http://localhost:8080/

...

Concurrency Level:      1
Time taken for tests:   60.004 seconds
Complete requests:      23
Failed requests:        0
Total transferred:      77747 bytes
HTML transferred:       55798 bytes
Requests per second:    0.38 [#/sec] (mean)
Time per request:       2608.884 [ms] (mean)
Time per request:       2608.884 [ms] (mean, across all concurrent requests)
Transfer rate:          1.27 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       0
Processing:   177 2609 1619.2   2894    7586
Waiting:      176 2608 1619.1   2893    7585
Total:        177 2609 1619.2   2894    7586

Percentage of the requests served within a certain time (ms)
  50%   2844
  66%   3064
  75%   3230
  80%   3256
  90%   3323
  95%   3658
  98%   7586
  99%   7586
 100%   7586 (longest request)

2.5秒超えるのか。。。
一応、opecache使ってファイルはキャッシュしているはずなんですが。

preloadあり

$ ab -t 60 -c 1 http://localhost:8080/

...

Concurrency Level:      1
Time taken for tests:   60.160 seconds
Complete requests:      301
Failed requests:        0
Total transferred:      1017565 bytes
HTML transferred:       730226 bytes
Requests per second:    5.00 [#/sec] (mean)
Time per request:       199.867 [ms] (mean)
Time per request:       199.867 [ms] (mean, across all concurrent requests)
Transfer rate:          16.52 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:   148  200 140.9    179    1575
Waiting:      148  199 140.9    179    1574
Total:        148  200 140.9    179    1575

Percentage of the requests served within a certain time (ms)
  50%    179
  66%    187
  75%    193
  80%    199
  90%    209
  95%    224
  98%    260
  99%    852
 100%   1575 (longest request)

ほんまか?
10倍強の速度になっているんだが。

まとめ

速度強化が劇的すぎて、流石にコンテナ特有のボリュームアクセス遅い問題に関係しちゃっているのかもしれないなぁとか思いながらも、ファイルアクセスが減って早くなっているのは事実だなぁって思いました。
将来的にはフレームワークごとにpreloadのヘルパーを用意してくれると、こっちとしては脳死で実装できて楽だなぁって他力本願に考えちゃったりしています。

今回はこんなところです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Wordpressの絶対パス、相対パスのおはなし

はじめに

ウェブサイトを使っていれば必ず聞くコトバに「パス」というのがある。
はじめたてのルーキープログラマーの中には「パス?なんじゃらほい」という人も少なくないだろうが、遅かれ早かれこれは覚えることになる。

というのも。
画像をサイトに表示したいとか、aタグで別ページにリンクするボタンを作りたいとか、ある特定の外部CSSや外部Jsファイルを読み込みたい、そういうときには必ずこのパスを書く必要が出てくるからだ。

たとえば、ウェブサイトで画像を表示したいとき、普通はこうする。

tatoeba.html
<img src="https://example.com/img/banner.jpg" width="100%" height"auto" alt="バナー" />

こんな風に、画像のある階層への「パス」……住所を記述するはずだ。
これをWordpressでやろうとすると、こうなる。

tatoeba.html
<img src="https://example.com/wp-content/themes/twentynineteen/img/banner.jpg" width="100%" height"auto" alt="バナー" />

いや、長くね?

こんなもん一々書いてたら頭が割れちまう。
だいいち、サイトを移管してドメイン(ここでは、https://example.comのこと)が別のやつになりましたーとか、テーマ名を変えることになりましたーみたいな事情ができて、結果的にhttps://syouzyoubae.com/wp-content/themes/daifuku/img/banner.jpgに書き換えなくちゃなりませんぜ旦那、
みたいなアクシデントが起こったらどうする。
サイトにある画像のURLが一から全部書き直しだ。

そんなもん嫌に決まってるので、ここは先人エンジニア達のありがたい知恵にすがることにしよう。

だいじょうぶ、先人プログラマーの方々は、この辺をラクに済ませる手法を確立してくれている。

対策その1:専用関数を使う

ありがたいことに、Wordpressにはこのパスを自動で出力してくれる関数が存在する。
「関数」というと難しそうに聞こえるが、要は「このコードが書かれた位置が、まるまる特定の文字列に置き換わる呪文」だ。そう思ってくれていい。
大きく分けて2通りあるので、時と場合に応じて使い分けよう。

ページのURLを出力したい場合

home_url()を使うことになる。

home_url()は、そのサイトのドメインを出力する関数だ。
ドメインは……「はじめに」でも触れたので掘り下げた説明はしないが、まあ乱暴な言い方をすると、「使っているサイトのトップページのURL」のことだと思ってくれればいい。
手書きで書いていった上のパターンとは異なり、サイトのドメインが変更されるとこちらもそれに合わせて自動的に変更されるのがポイント。なので、サイトが移管するたびにいちいちドメインを書き換えたりしなくてすむわけだ。

実際の利用例では以下のようになる。
https://example.com/aboutというページに移動したい場合

<a href="<?php echo home_url(); ?>/about/" >このサイトについて</a>

出力

<a href="https://example.com/about/" >このサイトについて</a>

余談だが、home_url()と同じ動作をするコードは他にもいくつかある。

<?php echo bloginfo('home'); ?>
<?php echo bloginfo('url'); ?>
<?php echo bloginfo('siteurl'); ?>

この3つだ。
ただ、この3種は現在非推奨、早い話が近いうちに使えなくなる関数なので、よほどのことがない限りはhome_url()を利用しよう。

ちなみに、<?php echo get_permalink(); ?>という似ているようで別な内容「パーマリンク」を出力するコードが存在するが、
これはドメインを含めた、ある特定のページのURL全体を出力するものだ。
用途がまったくもって違うので、混同しないようにしよう。もちろん、この2つを同時に使うことはできないし使うこと機会もない。なぜかって?使ってみればわかる。

画像のアドレスを記述する場合/ファイルのアドレスを記述する場合

こちらの場合、get_template_directory_uri()を使うことになる。
利用例は以下の通り。

<img src="<?php echo get_template_directory_uri(); ?>/img/banner.jpg" width="100%" height"auto" alt="バナー" />

出力

<img src="https://example.com/wp-content/themes/twentynineteen/img/banner.jpg" width="100%" height"auto" alt="バナー" />

get_template_directory_uri()は、ドメインを含めたテーマまでのパスを出力する。

実はWordpressには画像やファイル(cssやjs、pdfファイルなど、サーバー上に置くもの全般)を呼び出すときはドメインの下にwp-content/themes/(テーマ名)/を付け足さなければならないという厄介な性質があり、その面倒くささを解消してくれるのがget_template_directory_uri()というわけだ。

こちらにもやはり非推奨コードがあり、それは以下の三つになる。

<?php echo bloginfo('template_directory'); ?>
<?php echo bloginfo('template_url'); ?>
<?php echo bloginfo('stylesheet_directory'); ?>

これらを見つけたらget_template_directory_uri()に置き換えてやろう。

余談:get_template_directory_uri()によく似たコード

get_template_directory()

こちらのサイトに詳しく載っている。

一言で説明すると、get_template_directory()はドメイン部分がサーバー内のディレクトリパスになる。」
見た方がはやいかも。まあ、間違えないように。

get_stylesheet_directory_uri()

ファイルパスを指定したいときは<?php echo get_template_directory_uri(); ?>を使えばいいと言ったな……ありゃあ嘘だ。
……いや、ウソとまでは言わないけれど、使用できないケースがある。

じつはget_template_directory_uri()'には**子テーマにでは正常に動作しない**という弱点があるのだ。
ここまでの文脈でお察しのとおり、
get_stylesheet_directory_uri()はそのような、子テーマにテーマファイルを置いているときに使うのだ。
子テーマ?テーマファイル??あぁ???という人は読み飛ばしてOK。
そういう人はとりあえず、
get_template_directory_uri()'が不具合を起こしたらget_stylesheet_directory_uri()を試してみる、くらいの認識があれば大丈夫だ。

参考文献

https://wemo.tech/330

page.php


対策その2:相対パスを利用する

パスの書き方には「相対パス」「絶対パス」の2種類がある。

URLを記述するとき

page.php

参考文献

https://wemo.tech/322
https://www.webcreatorbox.com/tech/japanese-wordpress-cheat-sheet

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Wordpressの絶対パスのおはなし

はじめに

ウェブサイトを使っていれば必ず聞くコトバに「パス」というのがある。
はじめたてのルーキープログラマーの中には「パス?なんじゃらほい」という人も少なくないだろうが、遅かれ早かれこれは覚えることになる。

というのも。
画像をサイトに表示したいとか、aタグで別ページにリンクするボタンを作りたいとか、ある特定の外部CSSや外部Jsファイルを読み込みたい、そういうときには必ずこのパスを書く必要が出てくるからだ。

たとえば、ウェブサイトで画像を表示したいとき、普通はこうする。

tatoeba.html
<img src="https://example.com/img/banner.jpg" width="100%" height"auto" alt="バナー" />

こんな風に、画像のある階層への「パス」……住所を記述するはずだ。
これをWordpressでやろうとすると、こうなる。

tatoeba.html
<img src="https://example.com/wp-content/themes/twentynineteen/img/banner.jpg" width="100%" height"auto" alt="バナー" />

いや、長くね?

こんなもん一々書いてたら頭が割れちまう。
だいいち、サイトを移管してドメイン(ここでは、https://example.comのこと)が別のやつになりましたーとか、テーマ名を変えることになりましたーみたいな事情ができて、結果的にhttps://syouzyoubae.com/wp-content/themes/daifuku/img/banner.jpgに書き換えなくちゃなりませんぜ旦那、
みたいなアクシデントが起こったらどうする。
サイトにある画像のURLが一から全部書き直しだ。

そんなもん嫌に決まってるので、ここは先人エンジニア達のありがたい知恵にすがることにしよう。

だいじょうぶ、先人プログラマーの方々は、この辺をラクに済ませる手法を確立してくれている。

対策その1:専用関数を使う

ありがたいことに、Wordpressにはこのパスを自動で出力してくれる関数が存在する。
「関数」というと難しそうに聞こえるが、要は「このコードが書かれた位置が、まるまる特定の文字列に置き換わる呪文」だ。そう思ってくれていい。
大きく分けて2通りあるので、時と場合に応じて使い分けよう。

ページのURLを出力したい場合

home_url()を使うことになる。

home_url()は、そのサイトのドメインを出力する関数だ。
ドメインは……「はじめに」でも触れたので掘り下げた説明はしないが、まあ乱暴な言い方をすると、「使っているサイトのトップページのURL」のことだと思ってくれればいい。
手書きで書いていった上のパターンとは異なり、サイトのドメインが変更されるとこちらもそれに合わせて自動的に変更されるのがポイント。なので、サイトが移管するたびにいちいちドメインを書き換えたりしなくてすむわけだ。

実際の利用例では以下のようになる。
https://example.com/aboutというページに移動したい場合

<a href="<?php echo home_url(); ?>/about/" >このサイトについて</a>

出力

<a href="https://example.com/about/" >このサイトについて</a>

余談だが、home_url()と同じ動作をするコードは他にもいくつかある。

<?php echo bloginfo('home'); ?>
<?php echo bloginfo('url'); ?>
<?php echo bloginfo('siteurl'); ?>

この3つだ。
ただ、この3種は現在非推奨、早い話が近いうちに使えなくなる関数なので、よほどのことがない限りはhome_url()を利用しよう。

ちなみに、<?php echo get_permalink(); ?>という似ているようで別な内容「パーマリンク」を出力するコードが存在するが、
これはドメインを含めた、ある特定のページのURL全体を出力するものだ。
用途がまったくもって違うので、混同しないようにしよう。もちろん、この2つを同時に使うことはできないし使うこと機会もない。なぜかって?使ってみればわかる。

画像のアドレスを記述する場合/ファイルのアドレスを記述する場合

こちらの場合、get_template_directory_uri()を使うことになる。
利用例は以下の通り。

<img src="<?php echo get_template_directory_uri(); ?>/img/banner.jpg" width="100%" height"auto" alt="バナー" />

出力

<img src="https://example.com/wp-content/themes/twentynineteen/img/banner.jpg" width="100%" height"auto" alt="バナー" />

get_template_directory_uri()は、ドメインを含めたテーマまでのパスを出力する。

実はWordpressには画像やファイル(cssやjs、pdfファイルなど、サーバー上に置くもの全般)を呼び出すときはドメインの下にwp-content/themes/(テーマ名)/を付け足さなければならないという厄介な性質があり、その面倒くささを解消してくれるのがget_template_directory_uri()というわけだ。

こちらにもやはり非推奨コードがあり、それは以下の三つになる。

<?php echo bloginfo('template_directory'); ?>
<?php echo bloginfo('template_url'); ?>
<?php echo bloginfo('stylesheet_directory'); ?>

これらを見つけたらget_template_directory_uri()に置き換えてやろう。

余談:get_template_directory_uri()によく似たコード

get_template_directory()

こちらのサイトに詳しく載っている。

一言で説明すると、get_template_directory()はドメイン部分がサーバー内のディレクトリパスになる。」
見た方がはやいかも。まあ、間違えないように。

get_stylesheet_directory_uri()

ファイルパスを指定したいときは<?php echo get_template_directory_uri(); ?>を使えばいいと言ったな……ありゃあ嘘だ。
……いや、ウソとまでは言わないけれど、使用できないケースがある。

じつはget_template_directory_uri()には子テーマにでは正常に動作しないという弱点があるのだ。
ここまでの文脈でお察しのとおり、get_stylesheet_directory_uri()はそのような、子テーマにテーマファイルを置いているときに使うのだ。
子テーマ?テーマファイル??あぁ???という人は読み飛ばしてOK。
そういう人はとりあえず、get_template_directory_uri()が不具合を起こしたらget_stylesheet_directory_uri()を試してみる、くらいの認識があれば大丈夫だ。

参考文献

https://wemo.tech/330
https://wemo.tech/322
https://www.webcreatorbox.com/tech/japanese-wordpress-cheat-sheet

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

herokuデプロイで10分であなたのアプリを世界に公開する手順【Laravel5.8】

今回はherokuを使った、Laravelプロジェクトを世界に手っ取り早く公開する手順をご紹介します。

実は今回僕も初の公開作業をしながら同時にこのqiitaを書いているので、これが公開されているということは動作は保証されます笑

herokuとはPaaS(Platform as a Service)と呼ばれるサービスで、アプリケーションを実行するためのプラットフォームです。

もう少しわかりやすく説明すると、サーバやOS、データベースなどの「プラットフォーム」と呼ばれる部分を、インターネット越しに使えるようにしてくれるサービスの一つです。

レンタルサーバーと似ていると思うかもしれませんが、レンタルサーバーとPaaSは「環境を貸してくれる」という意味では一緒なので、ほぼ同じと考えても問題はありません

herokuとは?初心者でも5分で分かる基本や特徴をまるっと紹介

大まかな流れか以下の通りです。

herokuに登録

heroku CLIダウンロード

herokuでアプリ作成

PostgreSQLへの接続設定

herokuでDBマイグレーション

heroku openで世界に公開!

それではいきましょう!

環境

MacOS Mojave 10.14.5(windowsのみなさんごめんなさい?‍♂️)
Laravel5.8.x
MAMP5.3で作成したLaravelプロジェクト

herokuに登録

こちらより各自登録しておいてください。

https://signup.heroku.com/jp

heroku CLI

まずはherokuコマンドを入れます。

$ brew tap heroku/brew && brew install heroku

念の為バージョン確認します。

$ heroku -v
heroku/7.35.0 darwin-x64 node-v12.13.0

okですね。

ターミナルからherokuログイン

cdコマンドでlaravelプロジェクトに移動してください。

$ cd /Applications/MAMP/htdocs/kanban

kanbanは僕のプロジェクト名で、MAMPなのでhtdocs以下になっています

さあheroku CLIでログインします。

$ heroku login

以下のようなメッセージが出たらq以外を押す

heroku: Press any key to open up the browser to login or q to exit: 

すると?

スクリーンショット 2019-11-24 9.29.02.png

こんなブラウザが出てくるので、Log in押します。

Logging in... done
Logged in as xxx@xxx.xxx

ログインできた!

HerokuのHTTPサーバー用コンフィグファイル作成

$ echo "web: vendor/bin/heroku-php-apache2 public" > /Applications/MAMP/htdocs/kanban/Procfile

ここは正直自分も行う理由があやふやです(^◇^;)
ProcfileはHerokuアプリの起動時に実行するプロセスを定義するためのファイルみたいです。

herokuでアプリを作成する

$ heroku create

スクリーンショット 2019-11-24 10.09.13.png

前者がアプリのURL、後者がgitのプッシュ先になります。
これから使う大切なものなので、控えておきます。

ここからgitでherokuのリモートリポジトリにプッシュしていきます。

$ git init

これでリポジトリを作成します。
そして実はLaravelプロジェクト直下に.gitという隠しディレクトリができています。
これ超重要です。

$ git add .

git add .コマンドはリポジトリにコミットする前に行うステージングを全ファイル分やってくれます。

$ git commit -m "initial commit"

ステージングしたファイルをコミットしています。

そしたら、ここで一回.git/configを開きます。

そして、先ほどターミナルに出たgitのリポジトリアドレスも確認してください。

[remote "heroku"]
  url = https://git.heroku.com/lit-plateau-44850.git

これを追記しますが、
ここのアドレスは人によって違ってきます。

これがないとリモートリポジトリがどれかわからないので、fatal errorが出るんですよね(再送2回)

では、

$ git push heroku master

herokuのmasterリポジトリにプッシュせしめるで候。

remote: Verifying deploy... done.
To https://git.heroku.com/lit-plateau-44850.git
 * [new branch]      master -> master

このようなメッセージが出たらherokuデプロイできています。

ここからは細々した設定をしていきます。

Herokuの環境変数

$ heroku plugins:install heroku-config
$ heroku config:set DB_DATABASE=laravel
$ heroku config:push

DB名Laravelは、僕はDBローカルのMySQLでDB名がLaravelだったので、herokuでも合わせました。

Herokuにpostgresqlを作成、接続

herokuが無料で提供しているのはPostgreSQLということなので、herokuでHeroku Postgresを作成し、接続します。

$ heroku addons:create heroku-postgresql:hobby-dev
$ php -r 'preg_match("/^postgres:\/\/(.+?):(.+?)@(.+?):(.+?)\/(.*?)$/", `heroku config:get DATABASE_URL`, $matches); `heroku config:set DB_CONNECTION=pgsql DB_HOST=$matches[3] DB_PORT=$matches[4] DB_DATABASE=$matches[5] DB_USERNAME=$matches[1] DB_PASSWORD=$matches[2]`;'

herokuでマイグレーション

それでは、herokuでマイグレーションを実行します。

$ heroku run "php artisan migrate"

その後、

Migration table created successfully.

と出たら、終了です。

先ほどターミナルに出てきたアプリのURL覚えてますか?

あれが公開URLなので、緊張の入力です...

スクリーンショット 2019-11-24 10.49.50.png

公開できた?

DBも機能してる。

ちなみにtechpitで作ったtrello風タスク管理アプリになります(https化し忘れた...)

http://lit-plateau-44850.herokuapp.com/

画像投稿機能とかtwitter Authとか入れたかったなぁ...

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Moodle 3.7 マニュアル - PHP をソースからコンパイルする

原文

PHP をソースからコンパイルする

このページは、Ubuntu あるいは Debian ベースのディストリビューション上で PHP をソースからビルドする方法を示します。その他の Unix や Linux のシステム上で PHP をコンパイルする基礎としても使えることは、疑いがないでしょう。

It is possible that you may need to do this because the latest versions of Moodle require reasonably new versions of PHP which may not be supported by the packages in your version of PHP - especially "long term support" versions.

This discussion was based on Ubuntu 10.04 which (at the time of writing) is the latest Ubuntu LTS. It does not carry a new enough PHP version for Moodle 2.1.

Contents

1 前提事項
2 Apache web server
3 追加のライブラリを入手する
4 ビルド環境
5 PHP の入手とビルド
6 Apache と PHP を configure する
7 テスト
8 トラブルシュート
9 PHP のアップデート
10 関連項目

1 前提事項

It is assumed that you will have a basic install of Ubuntu (10.04 in this case) without (specifically) PHP installed and possibly no Apache web server either. If you already have PHP installed (as a package) you will need to remove it first:

sudo apt-get remove php5

2 Apache web server

You can either install Apache from the Ubuntu packages (recommended) or compile it from source. Compiling from source is simple but you end up (using default settings) with a directory structure that is completely different from the Ubuntu packaged version. This is not covered further here.

To install the package version, it's just

sudo apt-get install apache2 apache2-dev

The -dev package is required in order to build PHP.

3 追加のライブラリを入手する

Moodle requires a significant number of optional PHP modules. Many of these require development libraries to be available on the system before PHP is compiled. This aspect is what makes building PHP from source tricky. If you would like a challenge, all of these can be downloaded as source packages and built from scratch but it is much easier to use the packaged versions. They are installed as follows...

sudo apt-get install \
    libxml2-dev \
    libcurl4-openssl-dev \
    libjpeg-dev \
    libpng-dev \
    libxpm-dev \
    libmysqlclient-dev \
    libpq-dev \
    libicu-dev \
    libfreetype6-dev \
    libldap2-dev \
    libxslt-dev \
    libssl-dev \
    libldb-dev

You may also need to create the following symbolic links as root on Linux:

ln -s /usr/lib/x86_64-linux-gnu/libldap.so /usr/lib/libldap.so
ln -s /usr/lib/x86_64-linux-gnu/liblber.so /usr/lib/liblber.so

Note that the above includes the clients for both the MySQL and PostgreSQL databases. You may not want to install both. You may need to include other libraries if you add further options to your PHP build. A Google search involving your Ubuntu version, the function and the word 'library' or 'development' will often turn up the correct package name.

4 ビルド環境

Ubuntu does not have all the compilers, linkers and libraries you need in a standard installation. If you have not compiled anything from source before this can be fixed by a single command...

sudo apt-get install build-essential

5 PHP の入手とビルド

The latest version can be downloaded from www.php.net. At the time of writing, this was 7.2.10 but new versions come out quite regularly. This should download as a .tar.gz file (e.g. php-7.2.10.tar.gz). Place this in a suitable location in (probably) your home folder and unpack the file.

tar -zxvf php-7.x.y.tar.gz
cd php-7.x.y 

The next step is to run the 'configure' program. This digs around your system and creates specific make files based on your particular configuration. It also specified all the optional modules that will be compiled in. The minimum for Moodle 2 (and 1.9) is as follows...

./configure \
  --prefix=/usr/local/php \
  --with-apxs2=/usr/local/apache/bin/apxs \
  --enable-mbstring \
  --with-curl \
  --with-openssl \
  --with-xmlrpc \
  --enable-soap \
  --enable-zip \
  --with-gd \
  --with-jpeg-dir \
  --with-png-dir \
  --with-mysqli \
  --with-pgsql \
  --enable-embedded-mysqli \
  --with-freetype-dir \
  --with-ldap \
  --enable-intl \
  --with-xsl \
  --with-zlib

If you have special requirements you may need others. To get the full list of possibilities you can do...

./configure --help

This should complete without errors and finishes with an obvious copyright notice in a box. If you do get errors, it is most likely to be due to missing libraries. Make sure you have added all the libraries described above (with apt-get). Failing that, Google is your friend.

Once that bit is done, it's time to do the actual compiling and linking. Simply do...

make

This (depending on how fast your machine is) will take some time and will end up with the phrase
Build complete.

It's then just a matter of installing the files...

sudo make install

Note: PHP_BINDIR is a value set on compile time. The path is set to the prefix used in configure (Linux equivalent: ./configure --prefix ).

6 Apache と PHP を configure する

The first step is to copy the PHP configuration file (php.ini) from the source distribution to where it will be read on Apache startup. There are two supplied versions, one for production systems and one for development systems. The latter display many more errors and warnings and should (for security reasons) only be used in a development environment, as its name suggests. To copy your chosen file, do the following (from the source directory)..

sudo cp php.ini-production /usr/local/lib/php.ini
sudo ln -s /usr/local/lib/php.ini /etc

The second line is optional and makes a link to the ini file in /etc (you can find it at /etc/php.ini). At this point you might want to edit the ini file to customise it for your needs. A very likely change is the file upload limits and the php memory_limit setting.

Apache's configuration file will now require some changes in order to correctly handle PHP files. Ubuntu's APache configuration is a little strange and could be a whole discussion by itself. Essentially, you need to add the following lines:

LoadModule php5_module  modules/libphp5.so
AddType application/x-http-php  .php
DirectoryIndex  index.php  index.html

The first two can be stuck at the end of /etc/apache2/apache2.conf and the DirectoryIndex by checking mods-available/dir.conf and ensuring that index.php is in the list. Strictly speaking, you would create a file in mods-available and enable it with the a2enmod command but this is left as an exercise!

Once this has been completed, you just need to restart Apache...

sudo /etc/init.d/apache2 restart

7 テスト

The easiest way to test, is to create a small test file in the root of your web served directory (/var/www), calling it something like test.php...

  <?php
    phpinfo();

Then access the page from a web browser using http://hostname.of.your.server/test.php. You should see a lengthy list of the PHP setup (which you might want to check). If you just see the php code, the three settings in the Apache config have not worked for some reason and will require checked. If you get some other error, read on...

8 トラブルシュート

The best advice I can give is to look at the logs. The error log for Apache is in /var/log/apache/error.log with the latest errors at the end. Always look here first. Most problems are caused by incorrect file or directory permissions or incorrect settings in the Apache configuration.

9 PHP のアップデート

There are really two possibilities. Firstly, you may want to rebuild php with extra configuration settings, or you may want to build a newer version of PHP. You can follow the installation instructions exactly as above, right up to and including sudo make install. After that, simply restart the Apache web server and you are done.

For a new version, just use the same ./configure command as you did with the older version (not absolutely guaranteed to work, but very likely). If you can't remember the full command look in (in the old version) a file called config.log in the build directory or run the 'phpinfo' test above and you will see the config command used right at the top.

10 関連項目

Installing Moodle 2.7 on CentOS 6.5 (with PHP 5.3.3)? Forum thread

カテゴリ:インストール

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

cakephp ワンタイムURL作成をメール送信

ワンタイムURL

一度あるいは、限られた時間あるいは回数しかGETリクエストを受け付けないメカニズムである。 目的は、アクセス制限されたリソースに対して未承認アクセスすることをより困難にすることにある。

何かのログインでパスワードを忘れた時にメールアドレスを入力してそのアドレス宛に再設定などを行うURLが送られてくるやつです。

基本的な流れ

① メールアドレスを入力してそのアドレス宛にメール送信
https://book.cakephp.org/2/ja/core-utility-libraries/email.html

App::uses('CakeEmail', 'Network/Email');
$content = array('url' => $url);
$email = new CakeEmail();
$email->from('example@test.com')
     ->to($post_email)
     ->template('text_email')//テンプレートファイル名指定
     ->viewVars($content)//text_email.ctp内の$url変数はコントローラ内のviewVarsメソッドで渡した値を利用
     ->subject('パスワード再設定')
     ->send();//メール送信

② 送信と同時にDBにランダムなトークンを保存する

$token = substr(str_shuffle('1234567890abcdefghijklmnopqrstuvwxyz'), 0, 16);//ランダムな文字列
$save_data = array('User' => array('token' => $ctoken));//保存するワンタイムトークン
$this->User->save($save_data);//配列で値を渡す

③ ②と同時に有効期限を設定する

$limit = time() + 1800;//有効期限30分

メールを送信した時間から1800秒(30分)足した時間

④ 受け取ったURLのチェックを行う

$key = $this->request->query['key'];//パラメータからcheck_flagを取得
$pass = $this->request->data['User']['password'];//新しいパスワード
$save_data = array('User' => array('password' => $pass, 'token' => null));//保存するデータ
if (intval($expire) < time() || $key !== $db_token'User']['check_flag']) {
    $this->Flash->error('このURLは無効です');
} else {
    if ($this->request->is('post')) {
         $this->User->save($save_data);
         $this->Flash->success('パスワードを更新しました');
    }
}

DBに保存されているトークンとURLのキーで受け取った値が一緒であるかチェックする
一致しない場合は無効なURLとなる
また、②で設定した時間が現在時間より大きい場合も無効なURLとなる。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

名前空間(namespace)の使い方

名前空間を使う場面

ファイルを複数に分割して読み込むとき。

使用目的

同一関数名やクラス名、メソッドなどのバッティングを防ぐ。
ファイルが増えたり、複数人での開発時などに有効。

使用例

呼び出し(require)元ファイル

<?php

// 必ず先頭で名前空間を宣言。バックスラッシュで階層を指定。
namespace MyApp\Class;

class User {
  public $name;
  public function __construct($name) {
    $this->name = $name;
  }
  public function greeting() {
    echo "Good morning, Mr $this->name!";
  }
}
?>

実行ファイル

<?php

require "User_class.php";

// use 名前空間名 (use MyApp\Class as Class)
use MyApp\Class;

$Tom = new Class\User("Tom");
$Tom->greeting();
?>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

wordpressの投稿日と最終更新日の違い

備忘録

  • 個別投稿ページとかで

投稿日

<?php the_time('Y.n.j');?>

最終更新日

<?php the_modified_time('Y.n.j');?>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPコーディング規約について知る

PHP-FIG

The PHP Framework Interop Gruop
https://www.php-fig.org/
ある程度のコーディングルールについて イイネ! がまとめられた場所

ACCEPTED なルール

PSR-1: Basic Coding Standard

概要

共有PHPコード間の高度な技術的相互運用性を確保するために必要な標準コーディング要素と見なされるべきもの

タグ

ファイルは、<?php ?> か <?= ?> のどちらかを使用する必要がある

文字コード

ファイルは、文字コードについてBOMなしUTF-8を使用する必要がある

Side Effects

ファイルは、以下のいずれかを行うべきだが、両方を行うべきではない
 クラス、関数、定数などを宣言する
 出力の生成、設定ファイルの変更など
Side Effects = クラス、関数、定数などの宣言に直接関連しないロジックの実行、ファイルを include するだけ
出力の生成、require または include の明示的な使用、外部サービスへの接続、ini設定の変更、エラーまたは例外の発行、グローバル変数または静的変数の変更、ファイルの読み取りまたは書き込みなどが含まれる
ini_set();
include "";
echo "";
など

function func() {}
は、declaration でありSide Effectsではない

上記を混ぜた状態でファイルを作成してはいけない

名前空間とクラス名

名前空間とクラスについては、「オートロード」PSRに従う必要がある → PSR-4
クラス名は、PascalCaseで宣言する必要がある (ExampleClassName など)

クラス定数、プロパティ、メソッド

クラス定数はすべてアンダースコア区切りで大文字で宣言する必要がある (EXAMPLE_CLASS_CONST など)
プロパティは推奨されるべきものはないが、命名規則は一貫して適用されているべき
メソッド名は、camelCaseで宣言する必要がある (exampleMethodName など)

PSR-12: Extended Coding Style

概要

PSR-1の拡張であるPSR-2の新しいバージョン

ファイル

全てのPHPファイルは、Unix LF 行末のみを使用する必要がある
全てのPHPファイルは、単一のLFで終了する非空白行で終了する必要がある
PHPのみを含むファイルでは、終了タグを省略する必要がある

行の長さ

行の長さにハードリミットがあってはならない
行の長さのソフトリミットは120文字でなければならない
行は80文字以下である必要がある
行の末尾に空白があってはならない
読みやすくするため、または明示的に禁止されている場合を除き、関連するコードブロックを示すために、空白行を追加することができる
1行に複数のステートメントを含めてはいけない

インデント

4スペースのインデントを使用する必要があり、インデントにタブを使用してはいけない

ステートメント、ネームスペース、インポートステートメントの宣言

PHPファイルのヘッダーは、いくつかの異なるブロックで構成される場合がある
存在する場合、以下の各ブロックは単一の空白行で区切られなければならず、空白行を含んではいけない
関連しないブロックは省略できるが、各ブロックは以下にリストされている順序でなければならない

開始タグ (<?php)
ファイルレベルのdocblock
1つ以上の宣言ステートメント
ファイルの名前空間宣言
1つ以上のクラスベースのuseインポートステートメント
1つ以上の関数ベースのuseインポートステートメント
1つ以上の定数ベースのuseインポートステートメント
ファイル内の残りのコード

ファイルにHTMLとPHPが混在している場合でも、上記のセクションを適用する
PHPの終了タグが存在し、HTMLとPHPが混在している場合でも、ファイルの先頭にこれらのタグがなければならない

深さが2を超える複合名前空間は使用しない

PHPの開始タグと終了タグの外側にマークアップを含むファイルで厳密な型を宣言する場合、宣言はファイルの最初の行にあり、PHPの開始タグ、厳密な型宣言と終了タグを含める必要がある (つまりの外側)

クラスは以下のような形にする

Class ClassName
{
    // コード
}

開始中括弧は独自の行になければならず、空白行が前後にあってはならない
閉じ括弧は独自の行になければならず、空白行を前においてはならない

プロパティと定数

すべてのプロパティで可視性を宣言する必要がある
すべての定数で可視性を宣言する必要がある
varキーワードは、プロパティを宣言するために使用してはいけない
ステートメントごとに複数のプロパティを宣言することはできない
保護された可視性またはプライベートな可視性を示すために、プロパティ名の前に単一のアンダースコアを付けてはいけない
つまり、アンダースコアプレフィックスは明示的に意味を持たない
型宣言とプロパティ名の間にはスペースが必要

メソッドと関数

すべてのメソッドで可視性を宣言する必要がある
保護された可視性またはプライベートな可視性を示すために、メソッド名の前に単一のアンダースコアを付けてはいけない
つまり、アンダースコアプレフィックスは明示的に意味を持たない
メソッド名と関数名は、メソッド名の後にスペースを入れて宣言しない
開始ブレースは独自の行に移動する必要があり、終了ブレースは本文に続く次の行に移動する必要がある
開き括弧の後にスペースがあってはならず、閉じ括弧の前にスペースがあってはならない

メソッドと関数の引数

引数リストでは、各コンマの前にスペースがあってはならず、各コンマの後にスペースが1つなければならない
デフォルト値を持つメソッドおよび関数の引数は、引数リストの最後に配置する必要がある
引数リストは複数の行に分割されてもいい
その場合、1段インデントされる
引数リストが複数の行に分割されている場合、閉じ括弧と開き中括弧は、それらの間に1つのスペースを入れて独自の行に一緒に配置する必要がある
以下のとおり

public function exampleMethod(
    example1 $arg1,
    example1 $arg2,
    example1 $arg3
} {
    // コード
}

戻り値の型宣言が存在する場合、コロンの後に型宣言が続く1つのスペースが必要
コロンと宣言は、2つの文字の間にスペースを入れずに、引数リストの閉じ括弧と同じ行になければならない
以下のとおり

public function exampleMethod(
    example1 $arg1,
    example1 $arg2,
    example1 $arg3
}: string {
    // コード
}

null許容型宣言では、疑問符と型の間にスペースがあってはならない
&引数の前に参照演算子を使用する場合、その後にスペースがあってはならない
可変長の3ドット演算子と引数名の間にスペースがあってはならない
参照演算子と可変長3ドット演算子の両方を組み合わせる場合、それらの2つの間にスペースがあってはならない

abstract, final, そして static

存在する場合、abstractandとfinalは可視性宣言の前になければならない
存在する場合、staticは可視性宣言の後に来る必要がある

メソッドおよび関数呼び出し

メソッドまたは関数呼び出しを行うとき、メソッドまたは関数名と開き括弧の間にスペースがあってはならず、開き括弧の後にスペースがあってはならず、閉じ括弧の前にスペースがあってはならない
引数リストでは、各コンマの前にスペースがあってはならず、各コンマの後にスペースが1つなければならない
引数リストは複数の行に分割されてもよい
その場合、1段階インデントされる

制御構造

制御構造キーワードの後に​​1つのスペースが必要
左括弧の後にスペースがあってはならない
閉じ括弧の前にスペースがあってはならない
閉じ括弧と開き括弧の間に1つのスペースが必要
構造体は一度インデントする必要がある
本体は、開きブレースの次の行になければならない
閉じ括弧は、本文の次の行になければならない

if, elseif, else

if ($expression1) {
    // コード
} elseif ($expression2) {
    // コード
} else {
    // コード
}

単一のキーワードに見えるように、else if の代わりにelseif を使用する必要がある
括弧内の式は、複数の行に分割できる (分割した場合は1段階インデントされる)
分割したとき、最初の条件は次の行になければならない
閉じ括弧と開き括弧は、それらの間に1つのスペースを空けて、独自の行に一緒に配置する必要がある
条件間のブール演算子は、両方の組み合わせではなく、常に行の先頭または末尾になければならない

while、do while

以下のとおり

while ($expression) {
    // コード
}

括弧内の式は、複数の行に分割できる
分割した場合、1段階インデントされる
分割した場合、最初の条件は次の行になければならない
閉じ括弧と開き括弧は、それらの間に1つのスペースを空けて、独自の行に一緒に配置する必要がある
条件間のブール演算子は、両方の組み合わせではなく、常に行の先頭または末尾になければならない
do while も以下のとおり

do {
    // コード
} while ($expression);

括弧内の式は、複数の行に分割できる
分割した場合、1段階インデントされる
分割した場合、最初の条件は次の行になければならない
条件間のブール演算子は、両方の組み合わせではなく、常に行の先頭または末尾になければならない
for文は以下のとおり

for ($i = 0; $i < 10; $i++) {
   // コード
}

括弧内の式は、複数の行に分割できる
分割した場合、1段階インデントされる
分割した場合、最初の式は次の行になければならない
閉じ括弧と開き括弧は、それらの間に1つのスペースを空けて、独自の行に一緒に配置する必要がある

foreach

foreach 文は以下のとおり
foreach ($iterable as $key => $value) {
// コード
}

try, catch, finally

try-catch-finally ブロックは以下のとおり

try {
    // コード
} catch (FirstThrowableType $e) {
    // コード
} catch (OtherThrowableType | AnotherThrowableType $e) {
    // コード
} finally {
    // コード
}

演算子

インクリメント/デクリメント演算子は、演算子とオペランドの間にスペースがあってはならない
型キャスト演算子は、括弧内にスペースを入れてはならない
すべての2進算術演算子、比較演算子、割り当て演算子、ビット単位演算子、 論理演算子、文字列演算子、および型演算子の前後には、少なくとも1つのスペースが必要
三項演算子の前後には ? と : の両方を囲む少なくとも1つのスペースが必要
条件演算子の中央のオペランドが省略されている場合以下のとおり

$variable = $foo ?: 'bar';

そのほか

AUTOLOADING
PSR-4: Improved Autoloading

INTERFACES
PSR-3: Logger Interface
PSR-6: Caching Interface
PSR-11: Container Interface
PSR-13: Hypermedia Links
PSR-14: Event Dispatcher
PSR-16: Simple Cache

HTTP
PSR-7: HTTP Message Interfaces
PSR-15: HTTP Handlers
PSR-17: HTTP Factories
PSR-18: HTTP Client

場所とか

https://www.php-fig.org/
https://www.php-fig.org/psr/
https://www.php-fig.org/faqs/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む