かもメモ

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

Mac OSX 開発環境セットアップのメモ (5) ApacheでPHPが動くようにする

PHPはphpbrewでインストールしました。

Macにデフォルトで入っているapacheを起動しないようにする

$ sudo apacehctl stop
$ sudo launchctl unload -w /System/Library/LaunchDaemons/org.apache.httpd.plist

homebrewでインストール

以前はhttpd24という名前だったのが、httpdに変更になってるっぽい。

$ brew search httpd24
No formula or cask found for "httpd24".
Closed pull requests:
Rename deprecated httpd22/httpd24 to httpd (https://github.com/Homebrew/homebrew-php/pull/4500)
...
$ brew install httpd

Apacheの起動・停止・再起動

起動

$ sudo apachectl start

停止

$ sudo apachectl stop
# 即座に停止
$ sudo apachectl -k stop

再起動

$ sudo apachectl restart
# 即座に停止して再起動
$ sudo apachectl -k restart

httpd.confでApacheの設定

httpd.confをエディタで開き編集します。
httpd.confの場所はhomebrewでインストールした場合

/usr/local/etc/httpd
ポートを80に変更
Listen 8080

Listen 80
localhostのルートにするディレクトリを指定

DocumentRootとその下にある<Directory>のパスをルートにしたいディレクトリに変更する。

DocumentRoot "/usr/local/var/www"
<Directory "/usr/local/var/www">

DocumentRoot "/Users/<your_name>/Documents/local"
<Directory "/Users/<your_name>/Documents/local">

.htaccessとかが使えるようにルートパスを設定した<Directory>内にあるAllowOverrideAllに変更

<Directory "/Users/<your_name>/Documents/local">
  # ... 中略
  # AllowOverride controls what directives may be placed in .htaccess files.
  # It can be "All", "None", or any combination of the keywords:
  #   AllowOverride FileInfo AuthConfig Limit
  #
  # AllowOverride None
  AllowOverride All # <- Allに変更
mod_rewrite を有効にする

mod_rewrite.soの読み込みのコメントアウトを解除する

# LoadModule rewrite_module lib/httpd/modules/mod_rewrite.so

LoadModule rewrite_module lib/httpd/modules/mod_rewrite.so
User・Groupの設定

多分権限的なアレ...の筈
UserをPCのユーザー名(/Users/<user_name>/のパスに出てる名前でたぶんOKっぽい)
GroupStaffに変更する

User Sites
Group daemon

User <user_name>
Group Staff
サーバー名をlocalhostに変更
#ServerName www.example.com:8080

ServerName localhost
httpd-vhosts.confを読み込むようにしておく

ヴァーチャルホストの設定ファイルextra/httpd-vhosts.confを読み込むようにコメントアウトを外します

# Virtual hosts
# Include /usr/local/etc/httpd/extra/httpd-vhosts.conf

# Virtual hosts
Include /usr/local/etc/httpd/extra/httpd-vhosts.conf

httpd-vhosts.confにデフォルトでサンプルが入っていて、そのままだとApache起動時に指定しているフォルダが無いと出てくるので、httpd-vhosts.conf無いのサンプルの設定は削除またはコメントアウトしておきます。

設定したlocalhostが動作しているか確認

ルートに指定したディレクトリに適当なindex.htmlを作成して、apacheを起動

$ sudo apachectl start

http://localhostにアクセスして作成したindex.htmlの内容が表示されていればOK
確認ができたらApacheを停止しておきます

$ sudo apachectl stop

ApacheサーバーでPHPを使えるようにする

LoadModuleでphpbrewでインストールしたphp_moduleが読み込む設定になっているか確認

LoadModule php7_module /usr/local/lib/httpd/modules/libphp7.2.9.so

php7_module の読み込み設定がhttpd.confに無かった

phpbrewでPHPをインストールする際に+apxs2を付けてApacheのバリアントをインストールする必要があったようです。(+defaultには含まれていなかったようです...)

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

PHPがビルドされた状態でもう一度インストールコマンドを実行すると追加したバリアントだけインストールされたみたいなのでこれで良しとしました。

インストールが完了すると自動的にhttpd.confLoadModule php7_module 〜が追記されていました。

/usr/local/lib/httpd/modules/libphp7.2.9.so

phpbrewだとlibphp.soのパスは/usr/local/lib/httpd/modules/になるようです。

PHPを有効にする

httpd.confの下記部分を編集・追記

<IfModule dir_module>
  DirectoryIndex index.html
</IfModule>

<IfModule dir_module>
  # `/`でアクセスした時にindex.phpも表示するようにする
  DirectoryIndex index.php index.html
</IfModule>

# 追記
<FilesMatch \.php$>
  SetHandler application/x-httpd-php
</FilesMatch>
# 又は
<IfModule php7_module>
  AddType application/x-httpd-php .php
</IfModule>

<FilesMatch \.php$> SetHandler application/x-httpd-php </FilesMatch>でも<IfModule php7_module> AddType application/x-httpd-php .php </IfModule>でも動作するようなのですが、違いがいまいち解っていませんいません...

ApacheサーバーでPHPが動作するか確認

DocumentRootで指定したルート下にinfo.phpファイルを作成します。(ファイル名はテキトーでOK)

info.php
<?php phpinfo(); ?>

Apacheを起動

$ sudo apachectl start

http://localhost/info.phpにアクセスしてphpbrewでインストールしたPHPの情報が表示されていればOK


[参考]

Apache - apacheの起動,停止,再起動に関するまとめ

Mac OSX 開発環境セットアップのメモ (5) phpbrewでPHPをインストール

フリーランスのお仕事だとレンタルサーバーにWordPressでホームページ作りたいってのがまだまだ多いのです。 MAMPやXAMPPはサーバーもセットで簡単にインストールできるのですが、稀にお客さんの用意しているレンタルサーバーのPHPMySQLのバージョンが違っていて(古くて)バグを出してしまうということがあるので、自分でバージョンを指定した環境が用意できるようにしておいた方が安全な事が多いです。(本番リリース後、環境依存のバグ辛い!)

PHPのバージョンを管理できるphpenvはrbenvとバッティングするらしくメンドそうだったので、phpbrewを使ってPHPの環境を作ることにしました。

  • homebrew, x-code インストール済み

PHPのビルドに必要なパッケージのインストール

先にhomebrewでPHPのビルドに必要なパッケージをインストールしておきます。
参照: Requirement · phpbrew/phpbrew Wiki · GitHub

$ brew install automake autoconf curl pcre bison re2c mhash libtool icu4c gettext jpeg openssl libxml2 mcrypt gmp libevent
$ brew link icu4c
$ brew link --force openssl
$ brew link --force libxml2

phpbrewのインストール

以前はhomebrewでインストールできたようですが、curlで直接ダウンロードする方法に変わっていたので、公式のドキュメントを参考にインストールしていきます。

$ curl -L -O https://github.com/phpbrew/phpbrew/raw/master/phpbrew
$ chmod +x phpbrew
$ sudo mv phpbrew /usr/local/bin/phpbrew
$ phpbrew init

ダウンロードして、アクセス権を変えて、/usr/local/binに移動させて初期化。

~/.bashrcにを開き下記のphpbrewのパスを追記して保存

# phpbrew
source ~/.phpbrew/bashrc

ターミナルにもパスを反映

$ source ~/.bashrc

phpbrewでパッケージを探すプレフィックスを設定

macports, homebrew, debian, ubuntu, またはカスタムパスが使える。phpbrewがパッケージを探す時にどれを使うかみたいな感じだと思います。
※ ターミナルにphpbrewのパスが反映されてない状態で実行するとphpbrewコマンドでエラーが出るようになるので注意が必要です。

homebrewをlookup prefixに設定

$ phpbrew lookup-prefix homebrew

PHPのインストール

phpbrewでインストールできるバージョンの一覧を表示
$ phpbrew known
phpをインストール
$ phpbrew install <version> +default

+defaultオプションをつけると、もっともよく使用されているバリアント(Variants)をまとめてインストールしてくれるようです。
バリアントの種類を確認する

$ phpbrew variants

バリアントを指定してインストールする場合はPHPのバージョンに続けて+キーワードを付けて+mysqlのように指定すればOKなようです。

$ phpbrew install <version> +<variant_name>
インストールしたPHPを使う

PHPの切り替え (switch version temporarily)

$ phpbrew use <version>

デフォルトで使用するPHPを指定 (switch default version)

$ phpbrew switch <version>

別のターミナルを開いてPHPのバージョンを確認

$ php -v

切り替えたバージョンが表示されていればOKです。

php.iniの設定

php.iniの場所を調べる

$ php --ini

phpbrewでPHPをインストールした際のphp.iniの場所は~/.phpbrew/php/php-<version>/etc/php.iniのようです。

timezoneの設定

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

composerのインストール

homebrewでcomposerをインストールしておきます。

パッケージのチェック

$ brew search composer
==> Formulae
composer

インストール

$ brew install composer

 

この後apcheの環境を作ってlocalhostphpMySQLが使えるようにしたいと思います。


[参考]

phpbrewとphpenvの比較

Mac Sublime Text 3 ESLint 導入メモ

ESLintをグローバルにインストール

$ npm install -g eslint

Sublime TextにSublimeLinterパッケージをインストール

  1. Sublime Textを起動しPackage Control(⌘+shift+P)を起動
  2. Installと入力しInstall Packageを選択
  3. SublimeLinterと入力しEnterを押してインストール

SublimeLinter-eslint パッケージをインストール

同じくSublime TextのPackage ControlからSublimeLinter-eslintと入力してインストール

SublimeLinter-eslint の設定を作成

Sublime Textの設定ファイルにLinterの設定を行わないとESLintがエディター上で動作しませんでした。

メニュー > Preferences > Package Settings > SublimeLinter > Setting を選択
設定ファイルが開くので、SublimeLinter Settings - User のコメントが有るUser設定に下記を記述

  1. eslint用の設定ファイルを作成
    eslint用の設定ファイルは中身はjson形式で勝手に作ってしまって構わないようです。(.eslintrc というファイルを作成しました)
  2. linters 内に eslint を作成し、args 内に1.で作成したeslint用の設定ファイルのパスを追加
  3. paths該当するOS内にeslintのあるパスを追加

eslintのパスは下記コマンドで見つけることができます。

$ which eslint
// SublimeLinter Settings - User
{
  "linters": {
    "eslint": {
      "@disable": false,
      "args": [
        "--config",
        "~/.eslintrc"
      ],
      "excludes": []
    },
  },
  "paths": {
    "linux": [],
    "osx": [
      "~/.nodebrew/current/bin"
    ],
    "windows": []
  }
}

これでSublime Textを再起動するとJSのファイルでESLintが動作しているかと思います。

エラーが出る場合

設定を保存して再起動したら下記のようなエラーが出る場合

Invalid settings in 'Packages/User/SublimeLinter.sublime-settings':
Additional properties are not allowed ('user' was unexpected)

SublimeLinter Ver.4から SublimeLinter Settings ファイルで"user"キーが廃止になったようで、Ver.3以前の設定ファイルなどを参考にすると、この"user"キーがあるのでこれが原因でエラーになってしまうようです。

// SublimeLinter Settings - User
{
  "user" {  // ← この囲いがSublimeLinter Ver.4からは不要
    "linters": {
      // 中略
    }
  }
}

eslint用の設定ファイル

作成したeslintの設定ファイル(~/.eslintrc)に何をエラーとして表示するかなどの設定を作成します。

{
  "extends": "eslint:recommended",
  "env": {
    "browser": true,
    "node": true,
    "es6": true
  },
  "globals": {
  },
  "rules": {
    "no-console": "warn",
    "no-var": "error"
    // ... 略
  }
}

作り込んでないのでざっくりとした理解ですが、大雑把に下記のようなイメージかと思います。

  • "extends": "eslint:recommended" ... ESLintの推奨設定を使用
  • "env" ... モジュールやライブラリで定義されている変数?
    • true (有効)
    • false (無効)
  • "globals" ... グローバル変数
    • true (書換え可)
    • false (書換え不可)
  • "rules" ... lintのルール
    • "off" (チェックしない)
    • "warn" (警告)
    • "error" (エラー)

Sublime Textの設定ファイルで読み込ませている.eslintrcはエディタ全体なので、どのプロジェクトにも適応されてしまうのでクリティカルな共通の設定に留め、詳細な設定は各プロジェクトごとに作成して読み込ませることができると良さそうです。

ESLintの設定読んである理解するのと、全体の設定(.eslintrc)とプロジェクトごとの設定をする方法を見つけて、次はコードフォーマッターのPrettierを入れたい。

Atomもっさりしてて結局使わなかったけど、そろそろVSCode試してみようかな…


[参考]

速習webpack 速習シリーズ

速習webpack 速習シリーズ

Googleスプレッドシート 月末までの日付で検索した一覧を取得したい

やりたいこと。

有効期限とかの日付の入っている次のようなデータのマスターシートがあるとして、検索月を入力したら有効期限が今月末までのデータをマスターシートから取得して一覧が表示されるようにしたい。

A B C D E F
区分 ID タイトル URL 有効期限 備考

特定のシートからデータを取得するにはIMPORTRANGE関数でスプレッドシートのデータを取得し、QUERY関数で表示するカラムを設定し、WHERE節で表示するデータの絞り込みを行えばOKです。
検索月を入力するセルの値から、当月末の日付を作成してWHERE節で有効期限セル <= 今月末とすれば良さそうです。

データを表示するシートは次のような想定です

A B
1 検索月を入力=> {入力エリア}
2 {データを取得する関数}

B1セルに月を入力し、A2セルでB1セルの値を元にデータを取得する関数を作成します。

1. 入力値から当月末の日付を作成する

まず、検索セルB1に入力された月から、該当する月の月末の日付を作成します。

1-1. DATE 関数で今月の日付データを作成する

入力値が月のみなので、DATE関数を使って日付の形式にします。

DATE
指定した年、月、日を日付に変換します。
DATE(年, 月, 日)

= DATE( YEAR(TODAY()), B1, 1 )

年数は YEAR(TODAY())で本日の日付から年だけを取り出しています。
日付は、後で月末日に変換するので、間違いのない1日を指定しています。

1-2. EOMONTH関数で月末の日付を取得する

EOMONTH
起算日から指定した月数だけ前または後ろの月の最終日の日付を返します。
EOMONTH(開始日, 月数)

第二引数を0にすると今月末が取得できます。

= EOMONTH( DATE( YEAR(TODAY()),B1, 1 ), 0 )

これで、検索条件に使う月末の日付が完成しました。

2. IMPORTRANGE関数でマスターシートからデータを取得する

IMPORTRANGE
指定したスプレッドシートからセルの範囲を読み込みます。
IMPORTRANGE(スプレッドシートキー, 範囲の文字列)

スプレッドシートキーはスプレッドシートのURL https://docs.google.com/spreadsheets/d/< spreadsheet_key >/ から取得できます。
今回はA列(区分)とF列(備考)は不要なので、B列〜E列を取得したいとします。第二引数の「範囲の文字列」はシート名!B:Eとなります。

マスターシートからデータ取得はこんな感じ。

= importrange("spreadsheet_key", "シート名!B:E")

3. QUERY関数で表示したいセルを絞り込み

QUERY
Google Visualization API のクエリ言語を使用して、データ全体に対するクエリを実行します。
QUERY(データ, クエリ, [見出し])

データは 2 のIMPORTRANGEで取得したデータになるので、次のような感じになります。

= query( importrange("spreadsheet_key", "シート名!B:E"), "SELECT Col1, Col1 ... WHERE ..." )

QUERY関数で別のシートのデータを参照させた場合、SELECT文内で列の指定はABではなく、取り出した範囲に対してCol1Col2という形式で記述しないとエラーになってしうようです。
この場合範囲をB:Eとしているので、Col1BCol2Cという扱いになります。

B:E列でD列(Col3)を除いて表示させるのは次のような感じ (長くなるので、SELECT文のみ書いています)

SELECT Col1, Col2, Col4

全列そのまま表示させるなら、* を使えばOKです

SELECT *

4. WHERE 節に条件を追加して有効期限が今月末までのデータのみを表示する

今回の例ではE列(Col4)が有効期限なので、WHERE Col4 <= 月末の日付とすれば良さそうです。

WHERE節で条件に日付を使う場合は

SELECT * WHERE Col4 <= date '2018-08-31'

のように date 'YYYY-MM-DD' という形式にする必要があります。 (この形式でないとエラーになる)

1.で作成した月末の日付をTEXT関数でYYYY-MM-DDの形式にフォーマットします。

TEXT
指定した表示形式に従って、数値をテキストに変換します。
TEXT(数値, 表示形式)

= TEXT( EOMONTH(DATE(YEAR(TODAY()),B1,1),0), "YYYY-MM-DD" )

これをWHERE節の条件に入れます。
QUERY関数のWHERE節内に他のセルの参照や式を入れるには、"& A1 &"の形式にします。 (QUERY関数内では、SELECT文全体が"(ダブルコーテーション)で囲まれているので、文字列連結の形にしないとエラー)

SELECT * WHERE Col4 <= date '"& TEXT( EOMONTH(DATE(YEAR(TODAY()),B1,1),0), "YYYY-MM-DD" ) &"'

 

最終的な関数は次のような感じになっているかと思います。

= query( importrange("spreadsheet_key", "シート名!B:E"), "SELECT * WHERE Col4 <= date '"& TEXT( EOMONTH(DATE(YEAR(TODAY()),B1,1),0), "YYYY-MM-DD" ) &"'" )

検索条件の月を変更しても意図したデータが表示できていればOKです。
これで、入力された月を条件に一致するデータだけが表示されるようになりました。


[参考]

Python3 自作モジュールのインポートにハマる

Python3 (3.6.5)でオレオレモジュールを作成してインポートしようとした際に結構ハマったのでメモ

importの基本

import <module>キーワードでモジュールをインポートする

import math
print( math.pi )
# => 3.141592653589793

モジュールのメソッドはmodule.methodで実行する

from <module> import <method> でインポートするとモジュール名を都度記述しなくてもメソッドの呼び出しができる

from math import pi, sqrt
print( sqrt(9) )
# => 3.0

from ... import ...でインポートする際にメソッド名が被ると、後からインポートしたものが有効になる

from math import pi
from my_modules import pi
pi # => my_modules.pi 

一度インポートされたモジュールは再インポートされない

モジュール echo.py

# echo.py
print( f'echo.py: name is {__name__}' )

実行ファイル base.py

# base.py
import echo
# => echo.py が実行される
print('---')
import echo
# => 再実行はされないので何も出力されない

実行↓

$ python base.py
echo.py: __name__ is echo
---

同じ階層にあるディレクトリ内のファイルをインポート

フォルダ構成

/root
  |- /modules
  |    |- echo.py
  |- base.py
# modules/echo.py
def say_name():
  print("I'm " + __name__)

import <ディレクトリ>.<ファイル名>でインポート

# base.py
import modules.echo
modules.echo()
# => I'm modules.echo

from <ディレクトリ> import <ファイル名>でインポート

# base.py
from modules import echo
echo.say_name()
# => I'm modules.echo

import <ディレクトリ>.<ファイル名>from <ディレクトリ> import <ファイル名>でのインポートはどの階層からbase.pyを実行しても問題なくインポートすることができるようです。実行位置ではなくファイルから相対パスで読み込まれるという認識で良いのでしょうか?(この部分正確にな挙動はまだ理解できていません)

$ python root/base.py
> "I'm modules.echo"

$ cd root
$ :root python base.py
> "I'm modules.echo"

$ cd modules
$ :modules python ../base.py
> "I'm modules.echo"

更に深い階層にあるファイルのインポート

フォルダ構成

/root
  |- /modules
  |    |- /sub
  |        |- echo.py
  |- base.py

import ... - ディレクトリを.で繋げる

# base.py
import modules.sub.echo
modules.sub.echo.say_name()
# => I'm modules.echo

from ... import ... - fromの後に.区切りでディレクトリを指定する。

# base.py
from modules.sub import echo
echo.say_name()
# => I'm modules.echo

from ... import ...でimportの後にディレクトリを.区切りで表記するとエラー

# base.py
from modules import sub.echo
# =>    from modules import sub.echo
# =>                           ^
# => SyntaxError: invalid syntax

実行ファイルの上の階層にあるファイルのインポート

フォルダ構成

.
|- /root
|    |- base.py
|- /modules
|    |- echo.py

Pythonでは実行ファイルより上の階層にアクセスできない!?

モジュールのインポートで階層を.で表しているのだと思い、次のようにしてみた所エラーになってしまいました。

# base.py
from ..modules import echo
# => ValueError: attempted relative import beyond top-level package
# base.py
import ..modules.edho
# => SyntaxError: invalid syntax

どうやらPythonは現在実行されているファイルをモジュールのルートとみなすという仕様があり、その結果ルートより上の階層が見えなくなっていてるためエラーになるようです。

sys.path にモジュールのあるパスを追加するとインポートできる

sys.pathにはモジュールを検索するパスのリストが入っており、ここにインポートしたいファイルのあるパスを追加しておけば、実行ファイルの上の階層であってもインポートすることが出来るようになるみたいです。

# root/base.py
import sys
sys.path.append('../modules')
print(sys.path)
import echo

どうやらsys.pathにあるパスは実行した位置を起点にして探すようで、上記のように実行ファイルからの相対パスで指定した場合/rootディレクトリ以外で実行するとエラーになってしまいます。

$ python root/base.py
> ['/Users/.../root', '/User/.../.pyenv/versions/3.6.5/lib/python3.6', ... , '../modules']
  # ↑ sys.path に '../modules' がそのまま入っている
> ...
>     import echo
> ModuleNotFoundError: No module named 'echo'

パスの問題を解決するには、環境依存にならないよう動的に絶対パスを取得する方法があれば良さそうです。

pathlib でモジュールの絶対パスを取得する

Python 3.4 以上ではpathlibを使って絶対パスを取得するのが良さそうです。

# root/base.py
import sys
import pathlib
# base.pyのあるディレクトリの絶対パスを取得
current_dir = pathlib.Path(__file__).resolve().parent
# モジュールのあるパスを追加
sys.path.append( str(current_dir) + '/../' )

# モジュールのインポート
import echo
echo.say_name()
# => I'm echo

pathlibで取得できるパスはpathlib.PosixPathオブジェクトなので、そのままパスを編集しようと文字列連結しようとするとエラーになってしまいます。文字列連結などでパスを編集する際にはstr()で文字列にする必要があるのがハマりどころかもしれません。
これで実行ファイルの上の階層のファイルも読み込めるようになりました。

 
node.jsやrubyphpにくらべて、自分の位置の絶対パスの取得や上の階層のファイルのインポートが面倒だな〜という印象を持ちました。何かしら意味があって実行ファイルをrootとして上の階層にアクセスできなくしている経緯があると思うのですが、そのあたり詳しく説明されているサイトを見つけることが出来なかったので、少しもやっとしています。
それと、上の階層のファイルを読み込むためにsys.path にモジュールのあるディレクトリのパスを渡すという方法がお作法的に良いのかどうかいまいち判断できていません。
お作法的なものがあれば知りたいです。 (このドキュメント読めとかあればお願いします。)

また__init__.pyなどでディレクトリ内のモジュールをまとめてインポートとか出来るようなので、そのあたりは追々試してしていきたいと思います。


[参考]

ペンギン・ハイウェイ (角川文庫)

ペンギン・ハイウェイ (角川文庫)

森見登美彦作品好きなので映画化うれしい。