かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

PHP phpbrewで古い環境のPHPを動作させたメモ

別の人が作成した古いWordPressの改修の依頼が来て、環境つくりに時間がかかったのでメモをしておきます。
レンタルサーバーで動いているサイトだったのでPHP + Apache + MySQLという構成です。

要件

  • <?, <?= という書き方が多用されているのでエラーにならないようにする
  • php.ini など極力触りたくない (戻すの忘れそうで面倒なので)

<?<?=という書き方を今までしたことなかったので、戸惑いました。
検索してみた結果

PHP では、短い形式の開始タグ <? も使えます (しかしこれはおすすめしません。というのも、この形式のタグは php.ini で short_open_tag を有効にするか --enable-short-tags オプションつきで PHP を configure した場合でないと使えないからです)。

7.0.0 ASP タグ (<%, %>, <%=) や script タグ (<script language="php">) が PHP から削除されました。
5.4.0 short_open_tag の設定にかかわらず、<?= は常に使えるようになりました。
出典: PHP: PHP タグ - Manual

という事のようです。
5.4.0 short_open_tag の設定にかかわらず、<?= は常に使えるようになりました。」と書かれているのですが、
PHP7で<?=のあるファイルの表示を試したら設定が悪いのか、<?=がそのまま文字列として出力されてしまったので、PHP7以前に戻してshort_open_tagをONにすれば、この古(いにしえ)WordPressのテーマを動作させることができそうです。

phpbrewでPHP56に切替える

PHPはphpbrewでインストールしています。

インストールできるPHPのバージョンを確認

$ phpbrew known
7.3: 7.3.1, 7.3.0 ...
7.2: 7.2.14, 7.2.13, 7.2.12, 7.2.11, 7.2.10, 7.2.9, 7.2.8, 7.2.7 ...
7.1: 7.1.26, 7.1.25, 7.1.24, 7.1.23, 7.1.22, 7.1.21, 7.1.20, 7.1.19 ...
7.0: 7.0.33, 7.0.32, 7.0.31, 7.0.30, 7.0.29, 7.0.28, 7.0.27, 7.0.26 ...
5.6: 5.6.40, 5.6.39, 5.6.38, 5.6.37, 5.6.36, 5.6.35, 5.6.34, 5.6.33 ...
...

PHP5.6.40をインストール

$ phpbrew install 5.6.40 +default +mysql +apxs2 +openssl

Apache, MySQLを使うので+apxs2, +mysqlバリアントを合わせてインストールします。
+apxs2オプションを付けると自動的にhttpd.conflibphpをロードする設定が追加されます。

(ビルドに時間がかかる....

PHPのバージョンを切替える

ビルドが完了したら使用するPHPのバージョンを切替えます。

インストールされているPHPの確認

$ phpbrew list
* php-7.3.1
  php-5.6.40

PHPを切替える

$ phpbrew use php-5.6.40
$ php -v
PHP 5.6.40 (cli) 

php.iniの設定

非推奨のshort_open_tagphp.iniには設定せず、タイムゾーンmysql.sockだけ設定します

php.iniの場所を調べる

$ php --ini
Configuration File (php.ini) Path: /Users/user_name/.phpbrew/php/php-5.6.40/etc

php.iniを編集

タイムゾーン date.timezoneで検索

[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
date.timezone = "Asia/Tokyo"

MySQL mysql.default_socketで検索

; Default socket name for local MySQL connects.  If empty, uses the built-in
; MySQL defaults.
; http://php.net/pdo_mysql.default-socket
pdo_mysql.default_socket= /usr/local/var/mysql/mysql.sock

; Default socket name for local MySQL connects.  If empty, uses the built-in
; MySQL defaults.
; http://php.net/mysql.default-socket
mysql.default_socket = /usr/local/var/mysql/mysql.sock

; Default socket name for local MySQL connects.  If empty, uses the built-in
; MySQL defaults.
; http://php.net/mysqli.default-socket
mysqli.default_socket = /usr/local/var/mysql/mysql.sock

pdo_mysql.default_socket, mysql.default_socket, mysqli.default_socket の3箇所に設定をしました。

Apache httpd.confの設定

PHPのモジュール(libphp)を先に入れていたPHP7をコメントアウトして、PHP5のものが使われるようにします

httpd.conf

# php7 Module
# 👇 コメントアウト
# LoadModule php7_module        libexec/libphp7.3.1.so

# php5 Module
LoadModule php5_module        libexec/libphp5.6.40.so

動作確認

ここまででapache上でPHPが動作しMySQLに接続できるか確認します。

Apacheを起動

$ sudo apachectl start

MySQLを起動

$ mysql.server start

localhostディレクトリ内に次のような確認用のPHPファイルを作成します。

<?php
// mysql 接続テスト
define('DB_DATABASE','test');
define('DB_USERNAME','root');
define('DB_PASSWORD','root');
define('PDO_DSN','mysql:host=localhost;dbname=' . DB_DATABASE);
try{
  $pdo = new PDO(PDO_DSN,DB_USERNAME,DB_PASSWORD);
  $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

  $stmt = $pdo->query('SHOW TABLES');
  while($res = $stmt->fetch(PDO::FETCH_ASSOC)) {
    var_dump($res);
  }
  $pdo = null;
} catch(PDOException $e) {
  echo $e->getMessage();
}

// PHP情報を表示
phpinfo();

localhostからこのPHPファイルにアクセスして、データベースのテーブルとPHPのバージョンがPHP Version 5.6.40と表示されていればOKです。

<? を許容する設定

問題になっているサイトのディレクトリのルートに.htaccessを作成しshort_open_tagを許可する設定を追加します。

php_flag short_open_tag on

 
これで問題になっていた<?, <?=が多用されていたWordPressのテーマをローカルで表示できるようになりました!

PHPのバージョンを7系に戻す場合は

  1. apache, mysqlを停止
  2. phpbrewで使用するバージョンを7に変更 phpbrew use php-7.3.1
  3. httpd.confphp7_moduleコメントアウトを外し、php5_moduleコメントアウト
  4. apache, mysqlを起動

の手順で戻すことができます。

使用するバージョンを切替える度に都度httpd.conf内のモジュール部分を変更しないといけないのが少しメンドクサイのですが、phpbrewを使えば比較的楽にPHPのバージョンを切替えて使うことができそうです。


[参考]

ESlint acyncとFetchAPIがエラーになる

簡単なESlintの設定で試してたら、acyncfetchでエラーにったのでメモ

.eslintrc

{
  "root": true,
  "extends": [
    "eslint:recommended"
  ],
  "env": {
    "es6": true,
    "node": true,
    "commonjs": true
  },
  "parserOptions": {
    "sourceType": "module"
  },
  "rules": {
    "no-console": ["warn"],
    "no-extra-parens": ["off"],
    "semi": [ 2, "always" ]
  }
}

acync/await

async () => ...

-> error Parsing error: Unexpected token =>

async function() => ...

-> error Parsing error: Unexpected token function

parserOptions"ecmaVersion": 2017を追加する

{
  // 略
  "parserOptions": {
    "sourceType": "module",
    "ecmaVersion": 2017 // <- 追加
  }
}

FetchAPI

fetch(api_url).then(...)

-> error 'fetch' is not defined no-undef

globalsfetchを追加する
"ecmaVersion": 2017ではダメっぽい。

{
  // 略
  "globals": {
    "fetch": false  // <- 追加
  }
}

eslintの設定ゴリゴリ書くのあまり好きじゃない(特にrules)んで、enves2017: trueとかでエラーにならないようにとかならないかな...


[参考]

これはリンツ

Ajax (XHR) まわりいろいろ試してみた。

前回FetchAPIを試してみたので、昨今のAjax? XHR?を色々ggって試してみました。(ホント簡単に

jQuery $.ajax

みんな大好きjQuery。お世話になった$.ajax!!
今もWordPressで"ほぅむぺぃじ"を作ったりするときには使うんじゃないでしょうか?

Promiseに似たDeferredオブジェクトが使えるようになってから非同期処理が書きやすくなった記憶です。

$.ajax({
  url: api_url,
  type: 'POST',
  dataType: 'json',
  data: {
    key: 'post_data'
  }
})
  .done(function(res, textStatus, jqXHR) {
    console.log(res); // 返されたデータ
  })
  .fail(function(jqXHR, textStatus, err){
    console.error(textStatus, err);
  });

typeでメソッドを指定し、dataに送信するデータを渡す、返されるデータ形式dataTypeで指定。という感じ。
返される値は$.ajax.done()の引数である関数の第一引数(data)に渡される。 jqXHRオブジェクトはXMLHttpRequestオブジェクトをjQueryで拡張したもの。 成功時のdone、失敗時のfail以外にもalwaysthenなど色々指定できるものがある。
cf. Deferred Object | jQuery API Documentation

XMLHttpRequest

いわゆるプレーンなJavaScriptの方法?

const onPostJSONbyXHR = (url, data) => {
  const jsonData = JSON.stringify(data);
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('POST', url);
    xhr.setRequestHeader("Content-Type", "application/json");
    xhr.onload = () => {
      console.log(xhr.status);
      const data = JSON.parse(xhr.response);
      return resolve(data);
    };
    xhr.onerror = () => {
      console.log(xhr.status);
      return reject(xhr.statusText);
    };
    xhr.send(jsonData);
  });
};
onPostJSONbyXHR(api_url, post_data)
  .then((res) => console.log(res))
  .catch((err) => console.error(err));

Promiseを使ってXMLHttpRequestJSONをPOSTする部分を関数にしました。
JSONやFromDataなど、何を送るかでxhr.setRequestHeaderのヘッダー情報を自分で設定したり、データ形式に気を配るなど色々大変です。後、タイプ数が多くなるのが地味に辛い…
cf. XMLHttpRequest.send() | MDN

FetchAPI

こちらも生JSといえば、生JSってことになるのですかね?

fetch(api_url, {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  // String or Object
  body: JSON.stringify(post_data)
})
  .then((res) => res.json() )
  .then((data) => console.log(data))
  .catch((err) => console.error(err));

改めて見てみるとjQueryAjaxっぽいし、ほぼ同等な感じで使えそう。
cf. Fetch 概説 | MDN

axios

ライブラリなのでnpmでインストールしてrequireするかCDNで読み込ませるかして使う。

npm
$ npm install axios

読み込み

const axios = require('axios');
CDN
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

or

<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
使い方

axios.get(url[, config]), axios.post(url[, data[, config]])な形式でリクエストを送れる

axios.post(api_url, {
  key: 'post_data'
})
  .then((res) => {
    // success
    console.log(res, res.data);
  })
  .catch(err) => {
    // error
    console.error(err);
  })
  .then(() => {
    // always
    console.log('> axios.post end');
  });

$.ajax, FetchAPIっぽい書き方もできる

axios(config)

axios({
  method: 'POST',
  url: api_url,
  data: {
    key: 'post_data'
  }
})
  .then((res) => console.log(res, res.data))
  .catch((err) => console.log(err))
  .then(() => console.log('> axios(config) end'));

axios(url[, config])

axios(api_url, {
  method: 'POST',
  data: {
    key: 'post_data'
  }
})
  .then((res) => console.log(res, res.data))
  .catch((err) => console.log(err))
  .then(() => console.log('> axios(url[, config]) end'));

superagent

こちらもライブラリ。結構メジャーらしいです。

npm
$ npm install superagent

読み込み

const request = require('superagent');
CDN
<script src="https://cdnjs.cloudflare.com/ajax/libs/superagent/4.1.0/superagent.min.js"></script>
<script>
const request = window.superagent;
</script>
使い方
request.post(api_url)
  .send({
    key: 'post_data'
  })
  .set('Accept', 'application/json')
  .then((res) => console.log(res, res.body))
  .catch((err) => console.error(err));

以前はPromiseライクでないfunction(err, res){...}でコールバックだったみたいですが、Promiseなthen, catchで書けるようになっていました。
v4ではOld-styleな.end(function(err, res){...})もサポートされているようです。

Old-style

request.post(api_url)
  .send({
    key: 'post_data'
  })
  .set('Accept', 'application/json')
  .end((err, res) => {
    if(err) {
      console.error(err);
    } else {
      console.log(res, res.body);
    }
  });

検索するとかつてはwrapperを作成してPromiseな書き方にしてしていたようです。

const postAPI = (post_data) => {
  return new Promise((resolve, reject) => {
    request.post(api_url)
      .end((err, res) => {
        if(err) {
          reject(err);
        } else {
          resolve(res.body);
        }
      })
  });
};
postAPI({key: 'post_data'})
  .then((res) => console.log(res))
  .catch((err) => console.error(err));

感想

ちょっと昔はjQueryAjax一択だった感覚なのですが、モダンな環境だと色々な選択肢があるなーと思いました。(ブラウザ間の差異が少なくなったから???)

フレームワークを使う場合はフレームワークに生えているリクエスト周りを使えばいいと思いますが、生えてなかったり、フレームワーク使ってなかったりする場合は個人的にFetchAPIaxiosWordPressでのほうむぺぃじ制作ならWordPressに入ってるjQuery使っての$.ajaxかなーという感覚です。

最近の傾向としては、このHTTP通信してJSONとかとってくるやつAjaxって言わないっぽい印象を持ちました。なんて呼ぶのが主流なんですかね? XMLHttpRequestからのXHR???
用語関係はソロフリーランスだと理解というか肌感が難しいデス。。。


[参考]

イラスト図解式 この一冊で全部わかるWeb技術の基本

イラスト図解式 この一冊で全部わかるWeb技術の基本