20190819のPHPに関する記事は13件です。

【php】メールフォーム

html フォームパーツ

<form action="sent.php" method="post">

  Emailを入力
  <input type="text" name="email">

  内容
  <textarea name="content"></textarea>

  <select name="fruit">
    <option value="apple">りんご</option>
    <option value="banana">ばなな</option>
    <option value="orange">みかん</option>
  </select>

  <input type="submit" value="送信">

</fomr>

$_POST

フォームで送信した値を受け取るには、「$_POST」を使う。
「$_POST」は連想配列になっている。[ ]の中に、<input>と<textare>のname属性に指定した値を入れることで、それぞれの送信した値を受け取る事ができる。

echo $_POST['name'];
echo $_POST['email'];
echo $_POST['fruit']; //選択されたvalueが入る

//$_POSTの中身は連想配列になっている
array(
  'name' => 'formで入力した値',
  'email' => 'formで入力した値';
)

//option
for($i = 1; $1 < 4; $i++){
  echo "<option value='{$i}'>{$i}</option>";
}

//上記のfor文は下記と同じ意味
echo "<option value='1'>1</option>"
echo "<option value='2'>2</option>"
echo "<option value='3'>3</option>"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【php】戻り値【return】

return

関数は値を返すことが可能。その値を戻り値と呼ぶ。
関数を実行した結果、その関数実行部分が戻り値に置き換わるイメージ。
「return」で指定する。

//関数の定義
function getSum($num1,$num2){
  return $num1 + $num2;
}

//関数の呼び出し
$num = getSum(2,3);
echo $num; //結果: 5

//実行されると、値が戻り値(2+3)に置き換わる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【php】関数の作成

関数の作成

重複する処理を一箇所にまとめることで、コードに変更があった時に関数の中身を変更するだけ良くなる為便利。

円の面積を求める

$radius1 = 3;
echo $radius1 * $radius1 * 3;

$radius2 = 5;
echo $radius2 * $radius2 * 3;

//上記は同じ処理を実行している。

//関数の定義
function printCircleArea($radius){
  echo $radius * $radius * 3;
}
//関数の呼び出し
printCircleArea(3); //結果: 27
printCircleArea(5); //結果: 75

関数の記述方法

関数を作るには「function 関数名(){処理}」の形式で記述する。関数名は自由。呼び出しは「関数名()」。

//関数の定義
function hello(){
 echo 'Hello, world!';
}

//関数の呼び出し
hello();
//結果: Hello, world!

引数有り

//関数の定義
function printSum($num1,$num2){
  echo $num1 + $num2;
}

//関数の呼び出し
printSum(8,14);
//結果: 22;
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【php】関数の作成【function】

関数の作成

重複する処理を一箇所にまとめることで、コードに変更があった時に関数の中身を変更するだけ良くなる為便利。

円の面積を求める

$radius1 = 3;
echo $radius1 * $radius1 * 3;

$radius2 = 5;
echo $radius2 * $radius2 * 3;

//上記は同じ処理を実行している。

//関数の定義
function printCircleArea($radius){
  echo $radius * $radius * 3;
}
//関数の呼び出し
printCircleArea(3); //結果: 27
printCircleArea(5); //結果: 75

関数の記述方法

関数を作るには「function 関数名(){処理}」の形式で記述する。関数名は自由。呼び出しは「関数名()」。

//関数の定義
function hello(){
 echo 'Hello, world!';
}

//関数の呼び出し
hello();
//結果: Hello, world!

引数有り

//関数の定義
function printSum($num1,$num2){
  echo $num1 + $num2;
}

//関数の呼び出し
printSum(8,14);
//結果: 22;
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【php】組み込み関数

組み込み関数: strlen

phpには便利な関数がもとから組み込まれており、それを組み込み関数と呼ぶ。組み込み関数「strlen」は文字列の文字数を返す。

echo strlen('string'); //文字列を返す
//結果: 6

$lang = 'japanese';
echo strlen($lang); //変数もok
//結果: 8

組み込み関数: count / rand

「count」は配列の要素の数を返す。
「rand」は1つ目の引数と2つ目の引数の間のランダムな整数を返す。

$data = array('team1','team2','team3','team4');
echo count('$data');
//結果: 4

echo rand(10,15);
//結果: 10~15のランダムな整数を返す
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPフレームワーク Yii の仕組み

はじめに

フレームワークの仕組みでフレームワークを単純化して仕組みをまとめた。このエントリでは、Yiiフレームワークを利用して実用レベルのフレームワークの仕組みを確認してみたい。
PHPフレームワーク Yii の使い方ではとりあえず動くものを作れるだけの知識を簡単にまとめた。このエントリでは、Yiiフレームワークを使いこなせるようになるために、仕組みについてもう少し掘り下げてみたい。

アプリケーションの設定

エントリスクリプトの中で構成情報$config)を読み込む。そしてアプリケーションの実体となるクラス(Application)をインスタンス化する。ApplicationのAPI Documentationはこちら

yii-application/frontend/web/index.php
// 1. 環境の切り替え
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');

// 2. アプリケーションの設定
require __DIR__ . '/../../vendor/autoload.php';
require __DIR__ . '/../../vendor/yiisoft/yii2/Yii.php';
require __DIR__ . '/../../common/config/bootstrap.php';
require __DIR__ . '/../config/bootstrap.php';

$config = yii\helpers\ArrayHelper::merge(
    require __DIR__ . '/../../common/config/main.php',
    require __DIR__ . '/../../common/config/main-local.php',
    require __DIR__ . '/../config/main.php',
    require __DIR__ . '/../config/main-local.php'
);

// 3. アプリケーションの実体
(new yii\web\Application($config))->run();

Advancedテンプレート参照。エントリスクリプトで着目するのは主に3点。

  1. 環境の切り替え
    上記は開発環境の場合の例。本番環境の場合は以下の通り。

    defined('YII_DEBUG') or define('YII_DEBUG', false);
    defined('YII_ENV') or define('YII_ENV', 'prod');
    
  2. アプリケーションの設定
    backend, common, frontendディレクトリに注目する。それぞれにconfigディレクトリがある。(Yiiのディレクトリ構成はこちらを参照。)
    backendというアプリケーションにのみ適用する設定はbackend/configに、frontendというアプリケーションにのみ適用する設定はfrontend/configに保存する。(そしてhogeというアプリケーションにのみ適用する設定はhoge/configに保存する。)アプリケーション間で共通の設定はcommon/configに保存する。
    ○○というアプリケーションのエントリスクリプトで○○/configとcommon/configの設定ファイルを読み込む。
    Yiiではオートローダをここで読み込む。

  3. アプリケーションの実体
    読み込んだ設定($config)をyii\web\Applicationの引数に指定してインスタンス化し、run()を実行する。GoFでいうところのfacadeパターンかしら。
    ちなみに、インスタンス化する際にpreInit()を呼び出して、$configの初期値を設定する。例えば、エントリスクリプトで読み込んだ$config中にviewコンポーネントの設定がない場合、preInit()の中でデフォルト値(['class' => 'yii\web\View'])が設定される。

yii-application/vendor/yiisoft/yii2/base/Application.php
  public function __construct($config = [])
  {
      (省略)

      $this->preInit($config);

      (省略)

      Component::__construct($config);
  }

  public function preInit(&$config)
  {
      (省略)

      // merge core components with custom components
      foreach ($this->coreComponents() as $id => $component) {
          if (!isset($config['components'][$id])) {
              $config['components'][$id] = $component;
          } elseif (is_array($config['components'][$id]) && !isset($config['components'][$id]['class'])) {
              $config['components'][$id]['class'] = $component['class'];
          }
      }
  }

  public function coreComponents()
  {
      return [
          'log' => ['class' => 'yii\log\Dispatcher'],
          'view' => ['class' => 'yii\web\View'],
          'formatter' => ['class' => 'yii\i18n\Formatter'],
          'i18n' => ['class' => 'yii\i18n\I18N'],
          'mailer' => ['class' => 'yii\swiftmailer\Mailer'],
          'urlManager' => ['class' => 'yii\web\UrlManager'],
          'assetManager' => ['class' => 'yii\web\AssetManager'],
          'security' => ['class' => 'yii\base\Security'],
      ];
  }

preInit()の後にComponent::__construct($config)を呼び出す。
yii-application/vendor/yiisoft/yii2/base/BaseObject.phpからBaseYii.phpに渡り、configure()を実行する。

yii-application/vendor/yiisoft/yii2/BaseYii.php
  public static function configure($object, $properties)
  {
      // $propertiesは元を辿れば$config
      foreach ($properties as $name => $value) {
          // Componentの__set等が発火する。
          $object->$name = $value;
      }

      return $object;
  }
yii-application/vendor/yiisoft/yii2/base/Component.php
  public function __set($name, $value)
  {
      // $name = 'components'の時、setComponents()が実行される。
      $setter = 'set' . $name;
      if (method_exists($this, $setter)) {
          // set property
          $this->$setter($value);

      (省略)
  }
yii-application/vendor/yiisoft/yii2/di/ServiceLocator.php
  public function setComponents($components)
  {
      foreach ($components as $id => $component) {
          // 例
          // $id = view
          // $component = ['class' => 'yii\web\View']
          $this->set($id, $component);
      }
  }

  public function set($id, $definition)
  {
      (省略)

      // 後にコンポーネントをインスタンス化する際に利用する。
      $this->_definitions[$id] = $definition;

      (省略)
  }

オートローダ

上記の通りエントリスクリプトでオートローダを読み込む以外にもオートロードを実装する方法を提供している。spl_autoload_register()でパスを直書きするのは汎用性に欠けるため、composerのような気の利いた仕組みをフレームワークに組み込んである。
composerを利用したオートロードの仕組みについて、参考エントリはこちら

ルーティング

yii\web\Applicationのrun()の中でURLを解釈してMVCに処理させるfunctionが呼び出される。

yii-applicaiton/vendor/yiisoft/yii2/base/Application.php
public function run()
{
    try {
        $this->state = self::STATE_BEFORE_REQUEST;
        $this->trigger(self::EVENT_BEFORE_REQUEST);

        $this->state = self::STATE_HANDLING_REQUEST;
        // ここに注目。この中でURLを解釈している。
        $response = $this->handleRequest($this->getRequest());

        $this->state = self::STATE_AFTER_REQUEST;
        $this->trigger(self::EVENT_AFTER_REQUEST);

        $this->state = self::STATE_SENDING_RESPONSE;
        $response->send();

        $this->state = self::STATE_END;

        return $response->exitStatus;
    } catch (ExitException $e) {

handleRequest()の実装はweb/Application.php

yii-application/vendor/yiisoft/yii2/web/Application.php
public function handleRequest($request)
{
    if (empty($this->catchAll)) {
        try {
            list($route, $params) = $request->resolve();

        (省略)

        $result = $this->runAction($route, $params);

        (省略)

runAction()の実装はbase/Module.php

yii-application/vendor/yiisoft/yii2/base/Module.php
public function runAction($route, $params = [])
{
    // コントローラのクラスをインスタンス化する。
    $parts = $this->createController($route);

    if (is_array($parts)) {
        list($controller, $actionID) = $parts;

        (省略)

        // コントローラのアクションを実行する。
        $result = $controller->runAction($actionID, $params);

        (省略)

        return $result;
    }

    (省略)

イベント

run()の$this->triggerにも注目してみる。アプリケーションの特定のタイミングでコードを挿入するイベントという仕組み。イベントと処理内容を予め定義し、登録しておくと、イベントが発生した時に、登録しておいたコードが実行される。ガイドはこちら

$this->stateは例外をキャッチした時に呼び出されるend()の中で参照する。アプリケーションの終わらせ方を制御する。

コントローラ

ガイドはこちら。ざっくりとした処理の流れは以下の通り。

1. 上記の通りhandleRequest()でURLを解釈して、コントローラ、アクション及びパラメータを特定する。
2. コントローラのクラスをインスタンス化する。
3. アクション(コントローラクラスのメソッド)を実行する。パラメータを渡して。

継承関係は以下の通り。

開発者が作ったコントローラ
↓
yii\web\Controller(コンソールアプリケーションの場合yii\console\Controller)
↓
yii\base\Controller
↓
yii\base\Component
↓
yii\base\BaseObject

例えばビューを呼び出すrender()はbase/Controller.phpで実装されている。

yii-application/vendor/yiisoft/yii2/base/Controller.php
public function render($view, $params = [])
{
    // $this->getView()のrender()に委譲している。
    // getView()により委譲先のビューオブジェクトを決定する。
    // $contentはviews/layouts/main.phpが参照する$content
    $content = $this->getView()->render($view, $params, $this);
    return $this->renderContent($content);
}

// レイアウト(例えばviews/layouts/main.php)に$contentを渡して描画する。
public function renderContent($content)
{
    $layoutFile = $this->findLayoutFile($this->getView());
    if ($layoutFile !== false) {
        // ビューのクラスのrenderFile()に委譲している。
        return $this->getView()->renderFile($layoutFile, ['content' => $content], $this);
    }

    return $content;
}

getView()

yii-application/vendor/yiisoft/yii2/base/Application.php
public function getView()
{
    return $this->get('view');
}
yii-application/vendor/yiisoft/yii2/base/Module.php
public function get($id, $throwException = true)
{
    if (!isset($this->module)) {
        return parent::get($id, $throwException);
    }

    // Moduleの親クラスはServiceLocator
    $component = parent::get($id, false);

    if ($component === null) {
        $component = $this->module->get($id, $throwException);
    }
    return $component;
}
yii-application/vendor/yiisoft/yii2/di/ServiceLocator.php
public function get($id, $throwException = true)
{
        (省略)

        // コンポーネントのオブジェクトを作成する。
        // 先の処理で$definitionに['class' => 'yii\web\View']が設定してあるからViewのオブジェクトが生成できる。
        return $this->_components[$id] = Yii::createObject($definition);

        (省略)
}

ビュー

ガイドはこちら

yii-appliation/vendor/yiisoft/yii2/base/View.php
// base/Controllerのrender()から呼び出される。
public function render($view, $params = [], $context = null)
{
    // 処理内容はメソッド名から察する。
    $viewFile = $this->findViewFile($view, $context);
    return $this->renderFile($viewFile, $params, $context);
}

// base/ControllerのrenderContent()からも呼び出される。
public function renderFile($viewFile, $params = [], $context = null)
{
}

ビューのクラスはコントローラと同じく階層構造になっている。
但し、開発者はyii\web\Viewを継承してビューのクラスを作成するのではなく、ビューのクラスからから読み込まれるfrontend/views/site/index.phpなどのファイルを作成する。

yii\web\View
↓
yii\base\View
↓
yii\base\Component
↓
yii\base\BaseObject

モデル

ガイドはこちら

モデルのクラスの継承関係はこんな感じ。

開発者が作ったモデル
↓
yii\db\ActiveRecord
↓
yii\db\BaseActiveRecord
↓
yii\base\Model
↓
yii\base\Component
↓
yii\base\BaseObject

コントローラの中でモデルのクラスをインスタンス化して(または静的に呼び出して)利用する。例えばユーザーの情報をDBから取得して表示するのは以下のような実装になる。

yii-appliation/backend/controllers/UserController.php
public function actionView($id)
{
    return $this->render('view', [
        'model' => $this->findModel($id),
    ]);
}

protected function findModel($id)
{
    if (($model = User::findOne($id)) !== null) {
        return $model;
    } else {
        throw new NotFoundHttpException(Yii::t('yii', 'Page not found.'));
    }
}

yii-appliation/backend/models/User.php
class User extends \yii\db\ActiveRecord
{
    // Userモデルに固有の処理をここに書く。
    // DBアクセスなど全てのモデルに共通の機能はActiveRecord等に実装されているそれらを利用する。
}
yii-application/vendor/yiisoft/yii2/db/BaseActiveRecord.php
public static function findOne($condition)
{
    return static::findByCondition($condition)->one();
}
yii-application/vendor/yiisoft/yii2/db/ActiveRecord.php
protected static function findByCondition($condition)
{
    // ここで呼出し元のモデル(例えばUserモデル)と関連づけられる。
    $query = static::find();

    if (!ArrayHelper::isAssociative($condition)) {
        $primaryKey = static::primaryKey();
        if (isset($primaryKey[0])) {
            $pk = $primaryKey[0];
            if (!empty($query->join) || !empty($query->joinWith)) {
                $pk = static::tableName() . '.' . $pk;
            }
            $condition = [$pk => is_array($condition) ? array_values($condition) : $condition];
        } else {
            throw new InvalidConfigException('"' . get_called_class() . '" must have a primary key.');
        }
    } elseif (is_array($condition)) {
        $aliases = static::filterValidAliases($query);
        $condition = static::filterCondition($condition, $aliases);
    }

    return $query->andWhere($condition);
}

public static function find()
{
    // 例
    // ActiveQuery::className() = 'yii\db\ActiveQuery'
    // [get_called_class()]     = 'backend\models\User'
    return Yii::createObject(ActiveQuery::className(), [get_called_class()]);
}

DBの接続

yii-application/vendor/yiisoft/yii2/base/Application.phpのgetDb()でDBに接続しにいく。DBの接続に必要な情報は、以下のような感じで設定する。設定の仕組みについてはアプリケーションの設定参照。

yii-application/common/config/main-local.php
return [
    'components' => [
        'db' => [
            'class' => 'yii\db\Connection',
            'dsn' => 'mysql:host=localhost;dbname=akou',
            'username' => 'akakin',
            'password' => 'gyagyagya',
            'charset' => 'utf8',
            'tablePrefix' => 'tbl_',
        ],
    ],
];
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Visual Studio CodeでPHPをデバッグする方法

実施環境

  • Windows10
  • Visual Studio Code 1.37.1(以下VSCode)がインストール済
  • xamppがC直下にインストール済(PHP 7.3.7)

手順

  1. VSCode設定ファイル追記
  2. VSCode拡張機能「PHP Debug」のインストール
  3. PHPデバッグツール「XDebug」のインストール
  4. 「XDebug」をVSCodeに紐づけ
  5. デバッグを行う

1. VSCode設定ファイル追記

VSCodeを立ち上げ
ファイル>基本設定>「settings.json」を検索>「settings.jsonで編集」をクリック

JSONファイルに以下の内容を追記します。

"php.validate.executablePath": "C:\\xampp\\php\\php.exe"
"php.validate.run": "onType"

2. VSCode拡張機能「PHP Debug」のインストール

Ctrl + Shift + X で拡張機能検索ウィンドウを開きます。
「PHP Debug」と入力するとパッケージが表示されるので「インストール」をクリックします。
ボタンが「アンインストール」になったら成功。

php-extension.png

3. PHPデバッグツール「XDebug」のインストール

C:\xampp\htdocsの階層にphpinfo.phpというファイルを作成し、以下のコードを記述。

<?php
    phpinfo();
?>

xamppのコントロールパネルを開き、Apacheを起動。
C:\xampp\xampp-control.exe


起動後、http://localhost/phpinfo.php にアクセスすると下記のような画面が表示されるので、Ctrl + A で全文コピー。

phpinfo.png

https://xdebug.org/wizard.php を開き、テキストボックスに貼り付け。
Analyse my phpinfo() outputボタンをクリックし、表示されたDLLファイルをダウンロード。

ddl-download.png

ダウンロードしたDLLファイルは下記ディレクトリ配下へ移動。
C:\xampp\php\ext

4.「XDebug」をVSCodeに紐づけ

C:\xampp\php\php.iniに以下のコードを追記します。
※ zend_extensionの値は3.でダウンロードしたDLLファイル名

...
[XDebug]
xdebug.remote_enable = 1
xdebug.remote_autostart = 1
xdebug.remote_connect_back = 1
xdebug.remote_port = 9000
zend_extension = C:\xampp\php\ext\php_xdebug-2.7.2-7.3-vc15-x86_64.dll

5. デバッグを行う

ファイル>フォルダーを開く>デバッグしたいファイルのある階層を選択>デバッグするファイルを開く>左メニューバーからデバッグボタンをクリック

「デバッグ開始」ボタン横のプルダウンから「PHP」を選択。
再度プルダウンより「構成の追加」を選択すると、launch.jsonが開くので下記のように修正。

{
    // IntelliSense を使用して利用可能な属性を学べます。
    // 既存の属性の説明をホバーして表示します。
    // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Listen for XDebug",
            "type": "php",
            "request": "launch",
            "port": 9000,
            "pathMappings": {
                "${workspaceRoot}": "${workspaceRoot}"
            }
        },
        {
            "name": "Launch currently open script",
            "type": "php",
            "request": "launch",
            "program": "${file}",
            "cwd": "${fileDirname}",
            "port": 9000,
            "pathMappings": {
                "C:\\xampp\\htdocs\\test": "${workspaceRoot}"
            },
            "runtimeExecutable": "C:\\xampp\\php\\php.exe"
        }
    ]
}

デバッグしたいファイルにブレークポイントを置く。
「Launch currently open script」が選択されていることを確認し、デバック開始ボタンを押下。

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

PHPで書かれたスクリプトをAWS Lambda上で定期実行する

AWS上で動いているシステムがあって、さらに定期実行したいPHPで書かれたスクリプトをどこかで実行することになった。
適当なマシン上で定期実行することもできるが、今回AWSを使っているのでAWS Lambdaで動かすことにしてみた。

以下ではスクリプトのサンプルとして、「AWS Lambdaの実行リージョンと同じリージョンにある、そのアカウントが持つEC2インスタンスのIDのリストを取得する」ものを実行することにする。

事前調査

AWS Lambdaをまだ使ったことがなかったので、最初に目的通りできそうか調査を行った。

というわけで行けそうなので進める。

デフォルト構成の調査

AWSマネジメントコンソールを使って、Lambda関数を"一から作成"の「カスタムランタイム」の「デフォルトのブートストラップを使用する」で1つ作ってみた。
「関数コード」を見ると、bootstrap, hello.sh, README.mdの計3ファイルが生成されたのが見える。

README.mdを読んでわかることは、

  • まず読むべきドキュメントは https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/runtimes-custom.html
  • 使いたい(自分で作った、あるいは誰かが提供している)カスタムランタイムはレイヤーに設定する。
  • このLambda関数が実行される際に、実際に実行されるのは「関数コード」でルートにあるbootstrapである。

なお、これはREADME.mdではなく後で実行してみてわかったことだが、ルートにbootstrapがなければレイヤーに含まれるbootstrapファイルが実行されるようだ。

続けてbootstrapを確認。

bootstrap
#!/bin/sh
set -euo pipefail

# Handler format: <script_name>.<function_name>
#
# The script file <script_name>.sh  must be located at the root of your
# function's deployment package, alongside this bootstrap executable.
source $(dirname "$0")/"$(echo $_HANDLER | cut -d. -f1).sh"

while true
do
    # Request the next event from the Lambda runtime
    HEADERS="$(mktemp)"
    EVENT_DATA=$(curl -v -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")
    INVOCATION_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)

    # Execute the handler function from the script
    RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA")

    # Send the response to Lambda runtime
    curl -v -sS -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$INVOCATION_ID/response" -d "$RESPONSE"
done

つまり、

  • http://\${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next をGETする。
  • そのレスポンスヘッダの中にLambda-Runtime-Aws-Request-Idがあるので、その値を取得しINVOCATION_IDとする。
  • 任意のスクリプトを実行する。
  • http://\${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$INVOCATION_ID/response にスクリプトの実行結果をPOSTする。
  • ここまでをwhileで延々とループさせる。

というのがbootstrapがやっていることであると読める。

AWSマネジメントコンソール上だと「関数コード」で設定できるハンドラというものがある。ここで設定した値は環境変数_HANDLERに入る。
ハンドラ名は"hello.handler"が初期設定である。そのため、bootstrapの

source $(dirname "$0")/"$(echo $_HANDLER | cut -d. -f1).sh"行および、

RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA")行はhello.shのhandler関数を呼んでいることになる。
hello.shの中身は以下なので、スクリプトの実行結果はJSONを期待されているようだ。

hello.sh
function handler () {
    EVENT_DATA=$1

    RESPONSE="{\"statusCode\": 200, \"body\": \"Hello from Lambda!\"}"
    echo $RESPONSE
}

結局こちらでやるべきことは以下となる。

  • レイヤーに https://github.com/stackery/php-lambda-layer に書かれているものを設定する。PHP 7.3なら「arn:aws:lambda:(リージョン):887080169480:layer:php73:3」。
  • bootstrapを修正してPHPのスクリプトを呼ぶようにする。
  • PHPのスクリプトはbootstrapから呼べる場所に置く。

Lambda側へ渡したいファイルの作成

というわけでbootstrapをPHPのスクリプトを呼ぶように修正してみる。

bootstrap
#!/bin/sh
set -euo pipefail

while true
do
    # Request the next event from the Lambda runtime
    HEADERS="$(mktemp)"
    EVENT_DATA=$(curl -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")
    INVOCATION_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)

    # Execute the handler
    /opt/bin/php -c "${LAMBDA_TASK_ROOT}/php.ini" "${LAMBDA_TASK_ROOT}/${_HANDLER}.php"
    if [ $? -eq 0 ]; then
      RESPONSE="{\"statusCode\": 200, \"body\": \"Success\"}"
    else
      RESPONSE="{\"statusCode\": 500, \"body\": \"Error\"}"
    fi

    # Send the response to Lambda runtime
    curl -sS -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$INVOCATION_ID/response" -d "$RESPONSE" > /dev/null
done

上記の通り/opt/bin/php -c "${LAMBDA_TASK_ROOT}/php.ini" "${LAMBDA_TASK_ROOT}/${_HANDLER}.php"としたので、スクリプトファイルはbootstrapと同じディレクトリに"(ハンドラ名).php"という名前で置くことになる。
もっとも、ファイル名は変化するわけではないのでハンドラ名なんて使わなくても良いのだが、設定必須項目が使われないのもちょっと、ということで。

ここでphp.iniも使うようにしている。
これは今回のサンプルスクリプトがsimplexml.soとjson.soを使うので、それらをロードする必要があるためである。
simplexml.soとjson.soは https://github.com/stackery/php-lambda-layer に書かれている通りカスタムランタイム側で用意してくれているので、これらをロードすれば良い。
内容は以下となる。extension_dirでsoファイルが置かれているディレクトリを指定しないとロードできなかった。
これもbootstrapと同じディレクトリに配置する。

php.ini
extension_dir=/opt/lib/php/7.3/modules
extension=simplexml
extension=json

説明をbootstrap側に戻す。
スクリプトの実行結果はスクリプトの実行時のリターンコードが0かどうかで中身を変えているだけに今回はしてある。

他にも元のbootstrapと比べて特に欲しくない情報は出力しないようにしている。
これは標準出力や標準エラー出力へのすべての書き出しがCloudwatch Logsに出力されるからである。
逆に言えば、スクリプト側ではログ出力したい情報は標準出力か標準エラー出力に書き出すようにしておくと良い。

また、 http://\${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next をGETした際のレスポンスボディ(EVENT_DATA変数に格納されるもの。JSONである)はまったく使わずに握り潰している。

今回のサンプルスクリプトファイルの内容は以下である。
先に書いた通り、単に「AWS Lambdaの実行リージョンと同じリージョンにある、そのアカウントが持つEC2インスタンスのIDのリストを取得する」だけのものとさせてもらっている。

script.php
<?php
require 'aws/aws-autoloader.php';
use Aws\Ec2\Ec2Client;

$ec2Client = new Ec2Client([
  'version' => 'latest',
  'region'  => $_ENV['AWS_REGION'],
]);

$reservations = $ec2Client->describeInstances()['Reservations'];
foreach ($reservations as $reservation) {
  echo $reservation['Instances'][0]['InstanceId'] . "\n";
}
?>

このスクリプトのファイル名をscript.phpという名前にしたので、ハンドラ名はscriptとなる。
AWS SDK for PHPを呼んでいるがインストールは https://docs.aws.amazon.com/ja_jp/sdk-for-php/v3/developer-guide/getting-started_installation.html の一番下、「ZIPファイルを使用したインストール」で行っている。
展開位置はbootstrapと同じディレクトリであり、つまりbootstrap, php.ini, script.phpの3ファイルが置かれているディレクトリにawsディレクトリが作られている。
なお、私はPHPをほとんど触ったことないのでComposerの使い方とか知らないが、一般にはComposerを使うものだと思われる。

Lambda側へファイルを渡すための準備

修正したbootstrapやphp.ini, script.php、それに展開したAWS SDK for PHPはLambda側に置く必要がある。
AWSマネジメントコンソールを使うなら「関数コード」にてzipにして渡したりできる。
今回はCloudformationを使う。その場合、zipをS3に置いておく必要がある。

zipにする時の注意だが、bootstrapはLinuxファイルシステムにおける実行権限がついていなければならない。
特にWindows上で作業する場合は注意すること。WSLを使って作業するなどで問題ないと思われるが。

また、ルートディレクトリがzip書庫内に含まれていてはならない。
私は以下のコマンドで圧縮している。
ここでlambda-phpはbootstrapやphp.ini, script.php, 展開したAWS SDK for PHPが置かれているディレクトリとする。

$ cd lambda-php; zip -r ../src.zip .; cd -

この作成したzip(ここではsrc.zipという名前にしている)をS3にアップロードするが、こちら側の注意点としては https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html のS3Bucketの項に書かれている通り、Cloudformationを実行する(=Lambda関数が作成される)リージョンと同じリージョンのバケットを使用しなければならないことである。

また、バケットは別のAWSアカウントのものでも良いが、その場合はCloudformationを実行するアカウントからsrc.zipがアクセス権限上ダウンロード可能になっていないとならない。簡単にはパブリックアクセス可能にしておくなど。

Cloudformationテンプレートの作成

Lambda関数の一式をCloudformationで作成するにあたり、 https://github.com/stackery/php-lambda-layer にはAWS SAMを使った例が出ている。
これをやりたいことに合わせて適当に修正したtemplate.ymlというファイルにしたものが以下である。
(先に言っておくと私はこの方法を使用していないので、やり方だけ書く)

template.yml
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Resources:
  IamRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          -
            Effect: "Allow"
            Principal:
              Service:
                - "lambda.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Path: "/"
      Policies:
        -
          PolicyName: "CreateLogPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Effect: "Allow"
                Action:
                  - "logs:CreateLogGroup"
                Resource: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*"
              -
                Effect: "Allow"
                Action:
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                Resource: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${AWS::StackName}-function:*"
        -
          PolicyName: "ScriptPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Effect: "Allow"
                Action:
                  - "ec2:DescribeInstances"
                Resource: "*"
  ServerlessFunction:
    Type: "AWS::Serverless::Function"
    Properties:
      FunctionName: !Sub "${AWS::StackName}-function"
      CodeUri: src
      Runtime: provided
      Handler: script
      Role: !GetAtt IamRole.Arn
      MemorySize: 128
      Timeout: 10
      Layers:
        - !Sub "arn:aws:lambda:${AWS::Region}:887080169480:layer:php73:3"
      Events:
        event:
          Type: Schedule
          Properties:
            Schedule: "cron(*/5 * * * ? *)"

Eventsは5分ごとに定期実行するための設定にしてある。なお、実際には20から30秒程度遅れて実行されるようだ。実行環境の起動に掛かる時間だろうか。
スケジュールはcronの時刻設定書式が使用できるが、 https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/events/ScheduledEvents.html の通り一般的なcronのものの書式とは違いがあり、年を指定でき、日か曜日の使わない方は"?"にするなどの必要がある。
IAM roleはCloudwatch Logsにログを出力するのを許可するもの(CreateLogPolicy)と、スクリプトで必要なEC2インスタンスの一覧を取得するのを許可するもの(ScriptPolicy)を設定してある。
カスタムランタイムを使用する場合、通常Runtimeを"provided"にしてLayersに使用するカスタムランタイムを設定する。
また、与えるメモリ量は最小の128MB、強制終了までの時間は10秒に設定している。

AWS CLIがインストールされた環境で、aws cloudformation package --s3-bucket (deployを実行するのと同じリージョンにある適当な存在するバケット名) --template-file template.yml --output-template-file output.ymlを実行するとCodeUriで指定したディレクトリの中にあるファイルを再帰的にzip圧縮してS3の--s3-bucketで指定したバケットにアップロードしてくれ(ファイル名はzipファイルのmd5のように見える)、Cloudformationに食わせられるテンプレートファイルを--output-template-fileに指定した名前で生成してくれる。
ただ、このzip作成時にbootstrapに自動的に実行権限を付けてくれたら嬉しかったのだがそうはいかなかった。

生成されたoutput.ymlの内容は以下である。

output.yml
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Resources:
  IamRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - lambda.amazonaws.com
          Action:
          - sts:AssumeRole
      Path: /
      Policies:
      - PolicyName: CreateLogPolicy
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
            - logs:CreateLogGroup
            Resource:
              Fn::Sub: arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*
          - Effect: Allow
            Action:
            - logs:CreateLogStream
            - logs:PutLogEvents
            Resource:
              Fn::Sub: arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${AWS::StackName}-function:*
      - PolicyName: ScriptPolicy
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
            - ec2:DescribeInstances
            Resource: '*'
  ServerlessFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName:
        Fn::Sub: ${AWS::StackName}-function
      CodeUri: s3://xxxxxxxxxxxx/6d54284568d5c9f126c866bc6483835d
      Runtime: provided
      Handler: script
      Role:
        Fn::GetAtt:
        - IamRole
        - Arn
      MemorySize: 128
      Timeout: 10
      Layers:
      - Fn::Sub: arn:aws:lambda:${AWS::Region}:887080169480:layer:php73:3
      Events:
        event:
          Type: Schedule
          Properties:
            Schedule: cron(*/5 * * * ? *)

この生成されたoutput.ymlを使用してaws cloudformation deploy --template-file output.yml --capabilities CAPABILITY_IAM --stack-name (適当なスタック名)を実行すると、Cloudformationを使用したデプロイが実行される。
https://github.com/stackery/php-lambda-layer ではこれらのコマンドはAWS SAM CLIを使用して実行されているが、インストールしてみてsam --helpするとsam packageaws cloudformation packageの、sam deployaws cloudformation deployのエイリアスであることが分かるので、たぶんSAM CLIをインストールする必要は実際にはないと思われる。

生成されたoutput.ymlはSAMテンプレート形式で記述されており、Cloudformationで実行時にTransformによりCloudformationテンプレート形式に変換される。
実行しないと実際に何になるのかがわからないのがちょっと嫌だったので、実行後にAWSマネージメントコンソールのCloudformationのところで見られる変換後のテンプレートを参考に、最初からCloudformationテンプレート形式で書くことにした、というのが先に書いた通り上記のSAMテンプレートを使用する方式を使わなかった理由である。
aws cloudformation packageを使っていないので、上記「Lambda側へファイルを渡すための準備」の通りに圧縮して作ったsrc.zipをS3(以下の例では「xxxxxxxxxxxx-(リージョン名)」というバケット)にアップロードしてある。

というわけで作成したCloudformationテンプレートが以下である。

AWSTemplateFormatVersion: "2010-09-09"
Resources:
  IamRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          -
            Effect: "Allow"
            Principal:
              Service:
                - "lambda.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Path: "/"
      Policies:
        -
          PolicyName: "CreateLogPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Effect: "Allow"
                Action:
                  - "logs:CreateLogGroup"
                Resource: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*"
              -
                Effect: "Allow"
                Action:
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                Resource: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${AWS::StackName}-function:*"
        -
          PolicyName: "ScriptPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Effect: "Allow"
                Action:
                  - "ec2:DescribeInstances"
                Resource: "*"
  LambdaFunction:
    Type: "AWS::Lambda::Function"
    Properties:
      FunctionName: !Sub "${AWS::StackName}-function"
      Code:
        S3Bucket: !Sub "xxxxxxxxxxxx-${AWS::Region}"
        S3Key: src.zip
      Handler: script
      MemorySize: 128
      Timeout: 10
      Role: !GetAtt IamRole.Arn
      Runtime: provided
      Layers:
        - !Sub "arn:aws:lambda:${AWS::Region}:887080169480:layer:php73:3"
  EventsRule:
    Type: "AWS::Events::Rule"
    Properties:
      ScheduleExpression: "cron(*/5 * * * ? *)"
      Targets:
        -
          Id: !Sub "${AWS::StackName}-rule-target"
          Arn: !GetAtt LambdaFunction.Arn
  LambdaPermission:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: "lambda:invokeFunction"
      Principal: "events.amazonaws.com"
      FunctionName: !Ref LambdaFunction
      SourceArn: !GetAtt EventsRule.Arn

SAMテンプレートのAWS::Serverless::Functionタイプが、CloudformationテンプレートだとAWS::Lambda::Function, AWS::Events::Rule, AWS::Lambda::Permissionの3つになる感じか。

これをaws cloudformation deploy --template-file (テンプレートファイル名) --capabilities CAPABILITY_IAM --stack-name (適当なスタック名)を実行すると、こちらでもCloudformationを使用したデプロイが実行される。
AWSマネジメントコンソールを使うなら"スタックの作成"でテンプレートファイルをアップロードし、「スタックの名前」を入力して「AWS CloudFormationによってIAMリソースが作成される場合があることを承認します。」のチェックを入れて「スタックの作成」を行えば同じことになる。

ここまでやってCloudwatch Logsを見ると5分置きに実行されているのがわかる。

lambda-cloudwatch-logs.png

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

webarenaでubuntu その18

概要

webarenaでubuntu18.04やってみた。
postfix3.3やってみた。
php7.2でやってみた。

インストール

sudo apt-get install php

サンプルコード

<?php
        mail("ubuntu", "php", "test");
?>

確認

mail

以上。

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

migrateメモ

LarabelでのDB作成時に、migrateというものがあったので、使ってみました。
チームで全く同じDBをつかいたいときなどは、migratefileをそろえておくとまったくおなじDBがつかえます。

DB作成

CREATE DATABASE DB名;

マイグレーション生成

php artisan make:migration テーブル名

テーブル作成

new_hoge_table.php
     public function up()
     {
         Schema::create('テーブル名', function (Blueprint $table) {
             $table->increments('カラム名');//autoincrement主キー
             $table->integer('カラム名')->unsigned();//int型つかえないのでmigrationの場合はinteger
             $table->string('カラム名', 文字数)->default("")->unique();//ユニークの書き方はこんな感じ
             $table->datetime('カラム名')->default(DB::raw('CURRENT_TIMESTAMP'));//デフォルトでINSERTした日付とってくる
             $table->boolean('カラム名')->default(0);//true or false

             // 外部キー
             $table->foreign('外部キー')
             ->references('参照カラム')
             ->on('参照テーブル名');
         });
     }


    public function down()
    {
        Schema::dropIfExists('テーブル名');
    }

おまじない
外部キー設定がうまくいかなかったら意味もなくこれつけてみる

$table->engine = 'InnoDB';
※カラムに関してはよくわからないけど、使えない型があるので注意。intとか使えないみたいです。

新規データベースならば、上のupメソッドを修正する

しかし、リリース後のDB定義修正の場合は、直接修正するのではなく、新たにファイルを作成し、修正を記述

fix_hoge_table.php
    public function up()
    {
        Schema::table('修正テーブル名', function (Blueprint $table) {
            $table->boolean('maintenance_flag')->after('start_date')->default(0)->nullable(false);
            $table->string('maintenance_message',40)->after('maintenance_flag')->nullable(true);
            $table->string('maintenance_message', 255)->change();
         });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('修正したいテーブル', function (Blueprint $table) {
        //追加したいカラム(修正はかかなくてもいいはず)
            $table->dropColumn('maintenance_flag');
            $table->dropColumn('maintenance_message');
        });
    }
}

マイグレーション実行

php artisan migrate

ロールバック

php artisan migrate:rollback

リセット

php artisan migrate:reset

初期データをSeedでセット
シーダー生成のためのartisanコマンドを叩く
php artisan make:seeder テーブル作成したクラス名TableSeeder

    public function run()
    {
        //
        DB::table('テーブル名')->insert([
            'id' => 300,
            'room_id' => 100,
            'status' => 1,
            'created' => date('Y-m-d H:i:s'),
            'updated' => date('Y-m-d H:i:s'),
        ],
        [
            'room_id' => 101,
            'status' => 0,
            'created' => date('Y-m-d H:i:s'),
            'updated' => date('Y-m-d H:i:s'),
        ],
    }

シーダクラスをコールする為の設定
シーダクラスの定義が完了したら、このシーダクラスをシーディング実行時にコール(呼び出す)できるようにします。

seedsディレクトリにあるもう一つのファイルDatabaseSeeder.phpに以下を記述します。

laravel/database/seeds/DatabaseSeeder.php
    public function run()
    {
        $this->call([
          作成したテーブルのクラス名TableSeeder::class,
        ]);
    }

シーディング実行

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

【Laravel】migrationで複合主キーのうち1つをincrementsに指定したい

idをオートインクリメント
iduser_idtypeを複合主キーに設定したい。

class CreateFoosTable extends Migration
{
    public function up()
    {
        Schema::create('foos', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id');
            $table->tinyInteger('type');
            $table->text('text');
            $table->timestamps();

            $table->primary(['id', 'user_id', 'type']);
        });
    }
}

マイグレーションを実行しようとするとエラーが出る

Syntax error or access violation: 1068 Multiple primary key defined

すでにプライマリーキーがすでに設定されているというエラー

$table->increments('id');で主キーを設定して
$table->primary(['id', 'user_id', 'type']);で再度複合主キーを設定しようとしている?

そう思ったので、idを一度integerに設定して主キーを設定した後にincrementsに変更してみた。

class CreateFoosTable extends Migration
{
    public function up()
    {
        Schema::create('foos', function (Blueprint $table) {
            $table->integer('id');
            $table->integer('user_id');
            $table->tinyInteger('type');
            $table->text('text');
            $table->timestamps();

            $table->primary(['id', 'user_id', 'type']);
        });

        Schema::table('foos', function (Blueprint $table) {
            $table->increments('id')->change();
        });
    }
}

requires Doctrine DBAL; install "doctrine/dbal".
doctrine/dbalを追加しろと言われたので追加

https://readouble.com/laravel/5.8/ja/migrations.html

カラム変更
動作要件
カラムを変更する前に、composer.jsonファイルでdoctrine/dbalを確実に追加してください。Doctrine DBALライブラリーは現在のカラムの状態を決め、指定されたカラムに対する修正を行うSQLクエリを生成するために、使用しています。

$composer require doctrine/dbal

できた。

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

php-master-changes 2019-08-18

今日は不要コードを削除する修正があった!

2019-08-18

beberlei: Cleanup unnecessary if guard clause to free buffer.

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

プログラマーがWordPressが使いにくい理由を整理してみた。

きっかけ

仕事でWordPressを扱っているが、WordPressは正直使いにくい。
趣味でRuby on Rails やLaravelに触れたことのある私だが、WordPressはどうも好きになれない。
構築は楽だし慣れれば早いんだけど、WP全体の仕様に理解ができない。
どうやら、プログラマー側から出てきた人間であればあるほどそう思うらしく、
PHPのフレームワークでWordPressしかやったことがない人間からすれば、別に違和感を感じないらしい。
その理由として極一部であるが、どうしてそう思うのか、整理してみた。

使いにくい理由

値取得とテキスト表示が別関数

例えば、サイトのタイトルを表示させたいときは

<?php the_title(); ?>

なんだけど、タイトルを関数に入れて表示させたいときは

<?php 
  $title =  get_the_title();
  echo $title 
?>

と、別の関数が用意されている。
全ての表記が統一されておらず、サイトのURLを示すhome_url()はechoをつけないと表示されない。カテゴリーは配列であるが、それが関数で分からないのがすごくややこしい。

テーマ内で色々指定するファイルがfunction.phpだけ

function.phpでは
・ 投稿設定(登校時にpタグを削除する等)
・ フロント表示設定(自動で表示されるmetaタグ削除等)
・ 管理画面表示設定(メニュー非表示・ログイン時アイコン設定等)
・ 独自関数設定
・ オリジナル投稿(カスタム投稿)設定
・ 投稿画面で使える関数を設定(ショートコード)
・ ウィジェット作成
・ プラグインの詳細設定
などなど、
設定しようと思えば、なんでも出来てしまう。
しかし、これらを設定する場所がfunction.phpしかない。
カスタマイズをすればするほど、ファイルが長くなり、5000行を軽く超えるファイルになる。
そうなると、修正したいときに探すだけで一苦労だし、ダブっているソースに気付きにくいのも同然である。

管理画面上でしか設定出来ないこともあるし、どっちでも設定できることがある煩わしさ

全部function.phpで設定出来るならば移植も楽なんだけど、そうではない。
固定ページは一度管理画面で作成しなくちゃならないし、
投稿数を適切に設定しないと投稿表示がうまく表示されない。
管理画面の設定なのか、function.phpの設定なのか、はたまたプラグインの衝突なのかわかりにくい。
バグによってはこれという解決方法が無いものもあるらしく、ため息が出てしまう。

独特の専門用語が多すぎる

変に言葉を変えなくてもいいようなところを、専門用語を使ってややこしくしている節があるように思える。

デフォルトの記事の区分分けは「カテゴリー」「タグ」で、
オリジナルで設定する場合は「タクソノミー」。
オリジナルの投稿設定を「カスタム投稿」と言って、
カスタム投稿内での区分分けは「カスタムタクソノミー」。
カテゴリーやタクソノミー毎の「スラッグ(カテゴリー毎のID)名」を「ターム」。
通常の投稿枠にプラスしてオリジナルの入力枠を増やすのは「カスタムフィールド

書いているだけでも混乱するし、言葉で言われたらもっと理解できない。
命名の仕方の統一性すらない。
「ターム」は「スラッグ名」じゃダメなの?
なぜ、「タクソノミー」と「オリジナルカテゴリー」に命名を分ける仕様にしている???

総括

ワードプレスは作り方が独特なように思う。
ノンエンジニアの為のフレームワークであり、Webデザイナーのための、コーダーの為のシステムって感じる。
PHPを学ぶよりも、一番最初にWordPressを学んだ人が、他でここの知識を転用出来たらいいんだけど、それが今のところ全くないんですよね。
WordPressの沼にハマった人は、そこから抜け出すのは大変だよなぁ。Wordpressの闇だよなぁ。
そのように思いました。

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