20190415のPHPに関する記事は15件です。

Docker php:7.3(7.2)-fpm-alpineベースでphp-ast extention導入

個人的にメンテしているdyoshikawa/laravelイメージでPhanを使えるようにしたかった。
php-ast導入が必要だったのでビルド方法の記録。

OGProgrammer/php 7.2 with ast phan argon support

基本的には上記を参考にした。

Dockerfile
RUN git clone https://github.com/nikic/php-ast.git \
    && cd php-ast \
    && phpize \
    && ./configure \
    && make install \
    && echo 'extension=ast.so' > /usr/local/etc/php/php.ini \
    && cd .. && rm -rf php-ast

こんな記述をして docker build を走らせると

Cannot find autoconf. Please check your autoconf installation and the
$PHP_AUTOCONF environment variable. Then, rerun this script.

エラー。

PECL::memcacheインストールしようとしたらphpizeでこけた(泣)
dockerでPHP7.3+Laravel環境を15分で作る~2019年版

どうもautoconfが足りてないようだ。

Dockerfile
+ RUN apk add autoconf
RUN git clone https://github.com/nikic/php-ast.git \
    && cd php-ast \
    && phpize \
    && ./configure \
    && make install \
    && echo 'extension=ast.so' > /usr/local/etc/php/php.ini \
    && cd .. && rm -rf php-ast

こうすると今度は下記のエラー。

configure: error: no acceptable C compiler found in $PATH

xdebug インストール時の error: no acceptable C compiler について

gccが足りていないということらしい。

Dockerfile
- RUN apk add autoconf
+ RUN apk add autoconf gcc
RUN git clone https://github.com/nikic/php-ast.git \
    && cd php-ast \
    && phpize \
    && ./configure \
    && make install \
    && echo 'extension=ast.so' > /usr/local/etc/php/php.ini \
    && cd .. && rm -rf php-ast

今度は下記。

configure: error: C compiler cannot create executables

g++が足りない。

Dockerfile
- RUN apk add autoconf gcc
+ RUN apk add autoconf gcc g++
RUN git clone https://github.com/nikic/php-ast.git \
    && cd php-ast \
    && phpize \
    && ./configure \
    && make install \
    && echo 'extension=ast.so' > /usr/local/etc/php/php.ini \
    && cd .. && rm -rf php-ast

そして下記。

make not found

makeがないということだろう。

Dockerfile
- RUN apk add autoconf gcc g++
+ RUN apk add autoconf gcc g++ make
RUN git clone https://github.com/nikic/php-ast.git \
    && cd php-ast \
    && phpize \
    && ./configure \
    && make install \
    && echo 'extension=ast.so' > /usr/local/etc/php/php.ini \
    && cd .. && rm -rf php-ast

これは通った。

完成形

FROM node:10-alpine AS node
FROM php:7.3-fpm-alpine
MAINTAINER dyoshikawa

# install packages
RUN apk add -U --no-cache \
    bash \
    git \
    curl-dev \
    libxml2-dev \
    postgresql-dev \
    libpng-dev \
    libjpeg-turbo-dev \
    zip \
    libzip-dev \
    unzip \
    gmp-dev

# install PHP extensions
RUN docker-php-source extract
RUN cp /usr/src/php/ext/openssl/config0.m4 /usr/src/php/ext/openssl/config.m4
RUN docker-php-ext-configure gd --with-png-dir=/usr/include --with-jpeg-dir=/usr/include
RUN docker-php-ext-install pdo\
    pdo_mysql \
    mysqli \
    pdo_pgsql \
    pgsql \
    mbstring \
    curl \
    ctype \
    xml \
    json \
    tokenizer \
    openssl \
    gd \
    zip \
    gmp \
    bcmath

# install php-ast
RUN apk add --no-cache gcc g++ make autoconf
RUN git clone https://github.com/nikic/php-ast.git \
    && cd php-ast \
    && phpize \
    && ./configure \
    && make install \
    && echo 'extension=ast.so' > /usr/local/etc/php/php.ini \
    && cd .. && rm -rf php-ast

# install composer
RUN curl -sS https://getcomposer.org/installer | php \
    && mv composer.phar /usr/local/bin/composer

# install composer plugin
RUN composer global require laravel/installer
ENV PATH=~/.composer/vendor/bin:$PATH
RUN composer global require hirak/prestissimo

# install dockerize
ENV DOCKERIZE_VERSION v0.6.1
RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
    && tar -C /usr/local/bin -xzvf dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
    && rm dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz

# add node.js npm
COPY --from=node /usr/local /usr/local
RUN apk add --no-cache python make g++
RUN rm /usr/local/bin/yarn /usr/local/bin/yarnpkg

# add user
RUN apk add sudo shadow
RUN groupadd -g 1000 dyoshikawa
RUN useradd -u 1000 -g 1000 dyoshikawa
RUN sed -e 's/# %wheel ALL=(ALL) NOPASSWD: ALL/%wheel ALL=(ALL) NOPASSWD: ALL/g' \
    -i /etc/sudoers
RUN sed -e 's/^wheel:\(.*\)/wheel:\1,dyoshikawa/g' -i /etc/group
RUN mkdir /home/dyoshikawa && chown 1000:1000 -R /home/dyoshikawa
RUN mkdir /work && chown 1000:1000 -R /work
WORKDIR /work
USER dyoshikawa

ENTRYPOINT []
CMD php artisan serve --host 0.0.0.0
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

素PHPでDIをやる(pimple)

素PHPでDIをやりたくなったのでpimpleで簡単にやってみました。
これがベストかは全くわかりません。

対象読者

DIという単語を聞いたことがあるひと
Laravelでサービスコンテナという単語を聞いたことがあるひと

用語の整理(諸説あります)

DI
外部から必要なインスタンスを注入すること

DIコンテナ
DIを便利に行う仕組み、箱。
設定を書いておくとよしなにやってくれるかんじ。

余談ですが、Laravelでもこの仕組があり、それは「サービスコンテナ」と呼ぶらしい。

まず、DIでないパターン

<?php

class Message
{
    private $message;

    public function __construct()
    {
        $this->setMessage();
    }

    private function setMessage (): void
    {
        $this->message = 'こんにちは世界';
    }

    public function getMessage(): string
    {
        return $this->message;
    }
}

class Call
{
    private $message;

    function __construct()
    {
        // 必要なインスタンスを得るためにクラスの内部でインスタンス化しています。
        // 注入にはなっていません。
        $this->message = new Message();
    }

    function callMessage () {
        echo $this->message->getMessage();
    }
}

$call = new Call();

$call->callMessage();

// こんにちは世界

手動DI

class Call
{
    private $message;

    function __construct(Message $message)
    {
        $this->message = $message;
    }

    function callMessage () {
        echo $this->message->getMessage();
    }
}

// ここで手動でインスタンスを注入しています。
$message = new Message();
$call = new Call($message);

$call->callMessage();
// こんにちは世界

手動DIがたくさんになるとめんどいです。

class Call
{
    private $message;

    function __construct(
        Message $message,
        Message2 $message2,
        Message3 $message3
    )
    {
        $this->message = $message;
        $this->message2 = $message2;
        $this->message3 = $message3;
    }

    function callMessage () {
        echo $this->message->getMessage();
        echo $this->message2->getMessage2();
        echo $this->message3->getMessage3();
    }
}

$message = new Message();
$message2 = new Message2();
$message3 = new Message3();

$call = new Call($message, $message2, $message3);

$call->callMessage();

// こんにちは世界こんにちは日本ハローアメリカ

pimpleを使ったコンストラクタインジェクションの例。コンストラクタ内で注入します。

// インストール
$ ./composer.phar require pimple/pimple ~3.0
<?php

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

use Pimple\Container;

$container = new Container();

$container['Message'] = function ($c) {
    return new Message();
};

$container['Message2'] = function ($c) {
    return new Message2();
};

$container['Message3'] = function ($c) {
    return new Message3();
};

// ここでインスタンスをコンテナに入れている感じです。
$container['call'] = function ($c) {
    return new Call($c['Message'], $c['Message2'], $c['Message3']);
};

class Call
{
    private $message;

    function __construct(
        Message $message,
        Message2 $message2,
        Message3 $message3
    )
    {
        $this->message = $message;
        $this->message2 = $message2;
        $this->message3 = $message3;
    }

    function callMessage () {
        echo $this->message->getMessage();
        echo $this->message2->getMessage2();
        echo $this->message3->getMessage3();
    }
}

// この部分を端折ることができます。
//$message = new Message();
//$message2 = new Message2();
//$message3 = new Message3();
//
//$call = new Call($message, $message2, $message3);


$container['call']->callMessage();
// こんにちは世界こんにちは日本ハローアメリカ

設定の記述量が多くて、便利:thinking:なのかという感じなのですが、別クラスに設定を書いておいて使いたいときに注入すればいいので便利だと思います。

副産物として、テストがしやすくなったり、インターフェイスをかませてインターフェイスを注入することでクラス同士を疎結合にして、保守性を上げるということができるようになるようです。

参考
PIMPLEA simple PHP
Dependency Injection Container
https://pimple.symfony.com/

フレームワークを作りながらLaravelのアーキテクチャを学ぶ / Learn Laravel's architecture while creating a framework
https://speakerdeck.com/fendo181/learn-laravels-architecture-while-creating-a-framework?slide=109

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

PHPでDIをやる(pimple)

PHPでDIをやりたくなったのでpimpleで簡単にやってみました。
これがベストかは全くわかりません。

対象読者

DIという単語を聞いたことがあるひと
Laravelでサービスコンテナという単語を聞いたことがあるひと

用語の整理(諸説あります)

DI
外部から必要なインスタンスを注入すること

DIコンテナ
DIを便利に行う仕組み、箱。
設定を書いておくとよしなにやってくれるかんじ。

余談ですが、Laravelでもこの仕組があり、それは「サービスコンテナ」と呼ぶらしい。

まず、DIでないパターン

<?php

class Message
{
    private $message;

    public function __construct()
    {
        $this->setMessage();
    }

    private function setMessage (): void
    {
        $this->message = 'こんにちは世界';
    }

    public function getMessage(): string
    {
        return $this->message;
    }
}

class Call
{
    private $message;

    function __construct()
    {
        // 必要なインスタンスを得るためにクラスの内部でインスタンス化しています。
        // 注入にはなっていません。
        $this->message = new Message();
    }

    function callMessage () {
        echo $this->message->getMessage();
    }
}

$call = new Call();

$call->callMessage();

// こんにちは世界

手動DI

class Call
{
    private $message;

    function __construct(Message $message)
    {
        $this->message = $message;
    }

    function callMessage () {
        echo $this->message->getMessage();
    }
}

// ここで手動でインスタンスを注入しています。
$message = new Message();
$call = new Call($message);

$call->callMessage();
// こんにちは世界

手動DIがたくさんになるとめんどいです。

class Call
{
    private $message;

    function __construct(
        Message $message,
        Message2 $message2,
        Message3 $message3
    )
    {
        $this->message = $message;
        $this->message2 = $message2;
        $this->message3 = $message3;
    }

    function callMessage () {
        echo $this->message->getMessage();
        echo $this->message2->getMessage2();
        echo $this->message3->getMessage3();
    }
}

$message = new Message();
$message2 = new Message2();
$message3 = new Message3();

$call = new Call($message, $message2, $message3);

$call->callMessage();

// こんにちは世界こんにちは日本ハローアメリカ

pimpleを使ったコンストラクタインジェクションの例。コンストラクタ内で注入します。

// インストール
$ ./composer.phar require pimple/pimple ~3.0
<?php

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

use Pimple\Container;

$container = new Container();

$container['Message'] = function ($c) {
    return new Message();
};

$container['Message2'] = function ($c) {
    return new Message2();
};

$container['Message3'] = function ($c) {
    return new Message3();
};

// ここでインスタンスをコンテナに入れている感じです。
$container['call'] = function ($c) {
    return new Call($c['Message'], $c['Message2'], $c['Message3']);
};

class Call
{
    private $message;

    function __construct(
        Message $message,
        Message2 $message2,
        Message3 $message3
    )
    {
        $this->message = $message;
        $this->message2 = $message2;
        $this->message3 = $message3;
    }

    function callMessage () {
        echo $this->message->getMessage();
        echo $this->message2->getMessage2();
        echo $this->message3->getMessage3();
    }
}

// この部分を端折ることができます。
//$message = new Message();
//$message2 = new Message2();
//$message3 = new Message3();
//
//$call = new Call($message, $message2, $message3);


$container['call']->callMessage();
// こんにちは世界こんにちは日本ハローアメリカ

設定の記述量が多くて、便利:thinking:なのかという感じなのですが、別クラスに設定を書いておいて使いたいときに注入すればいいので便利だと思います。

副産物として、テストがしやすくなったり、インターフェイスをかませてインターフェイスを注入することでクラス同士を疎結合にして、保守性を上げるということができるようになるようです。

参考
PIMPLEA simple PHP
Dependency Injection Container
https://pimple.symfony.com/

フレームワークを作りながらLaravelのアーキテクチャを学ぶ / Learn Laravel's architecture while creating a framework
https://speakerdeck.com/fendo181/learn-laravels-architecture-while-creating-a-framework?slide=109

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

Laravelでバリデーションにモックを仕掛ける

バリデーションにモックを仕掛ける

ファサードには下記のようにshouldReceiveメソッドが実装されていて楽にモックを仕掛けることができます。
私のケースではexistsでテーブルに値があるか確認していて、apiのテスト時にDBアクセスを避けたかったので利用しました。

shouldReceiveメソッドを使用し、Cacheファサードへの呼び出しをモックできます。これはMockeryインスタンスを返します。ファサードはLaravelのサービスコンテナにより管理され、依存解決されていますので、典型的な静的クラスよりもかなり高いテスタビリティーを持っています。

makeメソッドだけではなく正常系チェック用のpassesメソッドも範囲にして、返り値を設定することです。
existsを使って無い時でも別途バリデーションのテストをしている等であれば使ってもよさそう。

ExampleTest.php
        Validator::shouldReceive('make->passes')
            ->once()
            ->andReturn(true);

        $response = $this->json('GET', /example, $param);

(参考)

https://readouble.com/laravel/5.5/ja/mocking.html

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

Laravel/Lumenでバリデーションにモックを仕掛ける

バリデーションにモックを仕掛ける

ファサードには下記の説明にあるように最初からモックをできるヘルパがあるので、楽にモックを仕掛けることができます。しかしバリデーションに仕掛けるケースの記事がなかったので備忘録として。

ファサードはLaravelのサービスコンテナにより管理され、依存解決されていますので、典型的な静的クラスよりもかなり高いテスタビリティーを持っています。

https://readouble.com/laravel/5.5/ja/mocking.html

実装

重要なのはバリデーションのmakeメソッドだけではなく、正常系確認用にあるpassesメソッドも対象にして、返り値を設定することでバリデーションが実行される心配がなくなります。

ExampleTest.php
        Validator::shouldReceive('make->passes')
            ->once()
            ->andReturn(true);

        $response = $this->json('GET', /example, $param);

まとめ

今回はバリデーションでexistsルールを使ってテーブルに値があるか確認していて、APIのテスト時にはDBアクセスを避けたかったのでモックを利用してます。他にも複雑なカスタムバリデーションなどを組んでいる時などにも良いのではないかと考えてます。

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

Laravel/LumenでValidatorにモックを仕掛ける

バリデーションにモックを仕掛ける

Laravel/LumenのValidatorクラスにはファサードが用意されています。ファサードには下記の説明にあるように最初からモックできるヘルパがあり、楽にモック化することができます。しかしValidatorに仕掛けるケースの記事がなかったので備忘録として。

Laravelにはイベント、ジョブ、ファサードを最初からモックできるヘルパが準備されています。これらのヘルパは主にMockery上で動作する便利なレイヤーを提供しているので、複雑なMockeryのメソッドコールを自分で作成する必要はありません。

https://readouble.com/laravel/5.5/ja/mocking.html

実装

重要なのはバリデーションのmakeメソッドだけではなく、正常系確認用にあるpassesメソッドもつないで、返り値を設定することでバリデーションが実行される心配がなくなります。

ExampleTest.php
        Validator::shouldReceive('make->passes')
            ->once()
            ->andReturn(true);

        $response = $this->json('GET', /example, $param);

まとめ

今回はバリデーションでexistsルールを使ってテーブルに値があるか確認していて、APIのテスト時にはDBアクセスを避けたかったのでモックを利用してますが、他にも複雑なカスタムバリデーションなどを組んでいる時などにも良いと考えてます。(もちろん別途テストは必要)

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

MAMPでローカル開発から、xserverのphpmyadminを使って、ローンチするまで。

phpの学習として、MAMP環境でWebサービスをローカルで開発したものの、
いざローンチするとなると、どうしていいかわからない。
そんな時のためのノートです。

【想定状況】
・ MANPで、phpmyadminを使ってお理、すでにDBにデータが入っている
・ DBとの接続に、PDOオブジェクトを使用している
・ ブログやポートフォリオ等で、すでにxserverを使用している

xserver側でやること

ではまず、下準備。
MAMPはローカル環境用なので、外部サーバーのxserverでの準備です。

xserverのサブドメインの申請

すでに、ドメインを持ってい人は、サブドメインを作成しましょう。

http://yahoo.co.jp
がメインドメインだとすると、サブドメインは
http://◯◯.yahoo.co.jp
となります。

新しいドメインとは異なり、無料かつお手軽に作成することができます。

xserverの「サーバーパネル」から
サブドメインの設定追加を行って行きます。

まずは
サーバーパネルから、ドメイン>サブドメイン設定

サブドメイン1.png

ドメイン選択画面から、サブドメインを作成したいドメインを選択

スクリーンショット 2019-04-15 16.17.39.png

サブドメインの設定追加をクリックし
サブドメイン3.png

実際に使用していくサブドメイン名(任意の文字列)を入力、
右下の確認画面で、サブドメインの申請は終了です!
(コメントは入力しなくても大丈夫です。)

無事、サブドメインが認証されるまで1時間〜半日ぐらいかかります。
(深夜に行っても2時間くらいで反映されました。)

スクリーンショット 2019-04-15 16.30.29.png

xserverのphppmyadminの作成

次にローカル環境で使用していたDBを移行してくるためのDB設定を行います。
サーバーパネルの
データベース>MySQL設定へ

スクリーンショット 2019-04-15 16.15.50.png

MySQL追加からDBの名前を入力します。
なんでもいいですが、今回「Egg」としました。

MySQL1.png

そうするとMySQL一覧のところに
「_Egg」というデータベースができています。

最初は、アクセス権未所有ユーザーのところに
自分のユーザーネームがあるので、アクセス権所有ユーザーに変更をしておいてください。

MySQL2.png

また後々、
MySQL ホスト名というところを使っていきますので
メモ帳なんかにコピーしておきます。

ローカル環境周りでやること

次にやるべきことはDBの移行です。
今までもphpMyAdminを使用していましたが、
xserverに、新しく作りましたのでそちらに移動させていきます。

phpMyAdiminへの移行

もともと使用していたphpMayAdminから
移行させたいDBを選択し、エクスポートを行います。

 phpmyadmin.png

詳しいやり方は以下のサイトを参考にしました。

https://inthecom.net/488

各項目にチェックをつけていき、

スクリーンショット 2019-04-15 16.53.11.png

最終的に、sqlファイルとして保存し、
移行先であるxserverのphpmyadminにインポートすれば、
DBの移行は終了です。

PDOオブジェクトの編集

サブアドレスも発行し、DBの移行も終わり、
あとは、xserver内にドキュメントを写すだけ....ではありません。

重要なDB接続の
PDOを変更する必要があります。

変更前までは$dsnのmyssql以下は
ローカルで開発していたため、
hostがlocalhostもしくは127.0.0.1でしたが

先ほどxserveでの設定時に出てきたhost名を入力する必要があります。
またそれに合わせて、DBnameも変更する必要があります。

変更前

function dbConnect(){
  $dsn = 'mysql:dbname=egg; 
                host=localhost;
                charset=utf8';
  $user = 'root';
  $password = 'root';
  $options = array(
    PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
  );
  $dbh = new PDO($dsn, $user, $password, $options);
  return $dbh;
}

変更後

function dbConnect(){
  $dsn = 'mysql:dbname=▲▲_egg; 
          host=◯◯.xserver.jp;
          charset=utf8';
  $user = 'DBのユーザー名';
  $password = 'DBのパスワード';
  $options = array(
    PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
  );
  $dbh = new PDO($dsn, $user, $password, $options);
  return $dbh;
}

変更後の
dsn文のdbnameとhostは
上で自分で決めたDB名とホスト名

DBのユーザー名とパスワードは
xserverでphpmydminに入るときに使用するやつです。

FTPソフトを使用した移行

あとは、FTPソフトを使用して移行していくだけです。

やり方は以下を参考にCyberDuckを使用していきました。

https://showtaki.com/30days-trial-3/

ファイルさえ移行しておけば
DBの接続を失敗していない限り、正常にサブドメインのアドレスで
Webページが表示されているはずです。

【困った時】SQLのエラーの一覧

これを行う上で、SQLのエラーとだいぶにらめっこしたので
まとめておきます。

ホスト名のエラー

SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: hostname nor servname provided, or not known

DB名のエラー

SQLSTATE[HY000] [1044] Access denied for user example@% to database 

ユーザー名のエラー

SQLSTATE[HY000] [1045] Access denied for user ‘◯◯’@127.0.0.1 (using password: YES)

最後に

役に立ったら、
いいねください!

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

セッション機能(SESSION)

プログラミング初心者であるため、内容に誤りがあるかもしれません。
もし、誤りがあれば修正するのでどんどん指摘してください。

私は、立命館大学ロボティクス学科4回生です。
現在、未来電子テクノロジーでインターンを行なっています。
日々のプログラミングで気になったことをブログに書いています。

初めに

私は、データベース連携フォームを作っている際に、ファイル間のデータの受け渡しに困っていました。
そこで、今回は、一番引っかかったSESSION機能について簡単にお話しします。

SESSION機能とは

SESSION機能は、webサイトにアクセスして作業を行う一連のことを言います。
これだとわからなかったため、簡単に解釈すると、ログインしてから、ログアウトするまでのようなことです。
つまり、ある状態に入ることなのです。

SESSION機能を使う理由

Webで使われるHTTPプロトコルに、状態を維持する、管理する方法がないからです。
HTTPプロトコルとは、HTMLなどのコンテンツの送受信で用いられる通信プロトコルのことです。
また、Webページに、アクセエスした人、経緯、回数をHTTPプロトコルでは、保持できないのです。

私が使ったSESSION機能のコード

session_start();

$number = htmlspecialchars( $_SESSION["number"], ENT_QUOTES );
$name = htmlspecialchars( $_SESSION["name"], ENT_QUOTES );
$grade = htmlspecialchars( $_SESSION["grade"], ENT_QUOTES );
$other = htmlspecialchars( $_SESSION["biko"], ENT_QUOTES );

session_destroy();


まとめ

この記事だけでは、SESSION機能について、存在くらいしかわからないと思います。
ぜひ、興味がある方は、調べてみてはいかがでしょうか?

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

BaserCMSのカスタムフィールドプラグインを4系対応して機能追加してみた

BaserCMSにはPetitCustomFieldと言う結構高機能な3系用のカスタムフィールドプラグインがあるのですが、4系用の更新がされておらず止まったままなので使えるようにしてみました。それに合わせて幾つか機能を追加しています。

ファイルアップロード機能

とりあえず一番欲しいと思われるファイルアップロード機能を追加しました。
BaserCMS標準のアップロード機能を利用してアップロード出来るようにしているので、そこに追加された画像も含めて選択することが可能です。

up01.jpg

ファイル選択するとこのような感じになります。

up02.jpg

カスタムフィールドの設置する位置を追加

pos01.jpg

カスタムフィールドの表示位置が上と下しかなく、ちょっと位置が中途半端だったので【エディターの下】を選択できるようにしました。

管理画面のUI文言などを調整

使ったことある人しかわからないかもですが登録画面が少し迷子になりやすい感じなので、少しわかりやすく名称などを変更しました。

  • カスタムフィールド → フィールドグループ
  • フィールド → フィールド項目
  • ブログIDで表示 → ブログ名称で表示 など

登録したデータの表示

ヘルパー周りも自分もあまりよくわかっていないのですが、登録されたデータはアーカイブだと\$posts、単ページでは\$postとして紐づいているようです。

今回の修正したファイル

以下にforkして修正した内容を上げてあります。
https://github.com/BigFly3/PetitCustomField

あんまりデバッグ出来てませんが良かったらお試しください。

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

【PHP】CarbonのaddMonth()を使ってハマった話(CarbonとDateTimeクラスの仕様を今一度確認してみる)

TL;DR

特に理由がなければCarbonを用いて月の計算をする場合はaddMonthsNoOverflow()メソッドを使うようにしたほうがいい

DateTimeクラスの仕様に度々ひっかかる

Carbonを用いて月の計算を行っていると、どうやら28~31日のデータを扱うときに挙動がおかしいことに気が付きました
前もこんなのあったなーと思いつつ再現させる
(前:【PHP】ある月の初日と末日を取得する方法(DateTimeクラス & Carbon編)#要注意引数は年月日を渡しましょう - Qiita

検証

例:契約日が15日未満の場合は翌月の27日、15日以降なら翌々月の27日が請求月になる
みたいな場合の処理
(ここでは契約日が2018-01-31で考えます)
環境としてはLaravelのv5.5系に入っているCarbon(1.36)を使用して以下の例の処理を動かします

use Carbon\Carbon;

$array = [];
$contractDate = new Carbon('2018-01-31');

// 5回払い
for ($i = 0; $i < 5; $i++) {
    // 契約日が15日以降か
    if ((int)$contractDate->format('d') < 15) {
        $array['date'] = (new Carbon($contractDate))->addMonth($i + 1)->format("Y-m-27");
    } else {
        $array['date'] = (new Carbon($contractDate))->addMonth($i + 2)->format("Y-m-27");
    }

    $array['target_month'] = (new Carbon($array['date']))->format('Y-m');

    var_dump($i);
    var_dump($array);
}

↓以下、tinker(laravel用REPL)で実行した結果

int(0)
array(2) {
  ["date"]=>
  string(10) "2018-03-27"
  ["target_month"]=>
  string(7) "2018-03"
}
int(1)
array(2) {
  ["date"]=>
  string(10) "2018-05-27"
  ["target_month"]=>
  string(7) "2018-05"
}
int(2)
array(2) {
  ["date"]=>
  string(10) "2018-05-27"
  ["target_month"]=>
  string(7) "2018-05"
}
int(3)
array(2) {
  ["date"]=>
  string(10) "2018-07-27"
  ["target_month"]=>
  string(7) "2018-07"
}
int(4)
array(2) {
  ["date"]=>
  string(10) "2018-07-27"
  ["target_month"]=>
  string(7) "2018-07"
}
>>>

なんだこれは・・・

CarbonのaddMonth()について調べてみる

CarbonDateTimeクラスのラッパーなので、基本的にDateTimeクラスの仕様に依存してるみたいです。
んでaddMonth()の処理がどうなっているかCarbonのソースコード(Carbon/Carbon.php at version-1.36)を見てみると

Carbon/Carbon.php
    /**
     * Add a month to the instance
     *
     * @param int $value
     *
     * @return static
     */
    public function addMonth($value = 1)
    {
        return $this->addMonths($value);
    }

addMonths()メソッドを呼び、

Carbon/Carbon.php
    /**
     * Add months to the instance. Positive $value travels forward while
     * negative $value travels into the past.
     *
     * @param int $value
     *
     * @return static
     */
    public function addMonths($value)
    {
        if (static::shouldOverflowMonths()) {
            return $this->addMonthsWithOverflow($value);
        }
        return $this->addMonthsNoOverflow($value);
    }

static::shouldOverflowMonths()で呼ばれているmonthsOverflowプロパティはデフォルトでtrueになっているので、
オーバーフローで月を計算するメソッドaddMonthsWithOverflow()を呼びます。

Carbon/Carbon.php
    /**
     * Add months to the instance. Positive $value travels forward while
     * negative $value travels into the past.
     *
     * @param int $value
     *
     * @return static
     */
    public function addMonthsWithOverflow($value)
    {
        return $this->modify((int) $value.' month');
    }

でその中身はDateTimeクラスのmodify()メソッドでした。
ドキュメント(PHP: DateTime::modify - Manual)にも書いてある通り、月の加減算には注意ということで以下の処理例が載ってます。

例2 月の加減算には注意

<?php
$date = new DateTime('2000-12-31');

$date->modify('+1 month');
echo $date->format('Y-m-d') . "\n";

$date->modify('+1 month');
echo $date->format('Y-m-d') . "\n";
?>

上の例の出力は以下となります。
2001-01-31
2001-03-03

要は月の計算(加算)時に存在しない日(2月が28日までで、31日がない)になった場合、あふれた日数(3日)次の月(3月)+して計算する(=3月3日)という仕様になってるんですね

確認するためにmodify()メソッドを使用して処理を書いてみました

date_default_timezone_set('Asia/Tokyo');

for ($i = 0; $i < 10; $i++) {
    $date = new DateTime('2018-01-31');
    $date->modify((int) ($i + 2).' month');
    var_dump($date->format('Y-m-d H:i') ." → " . $date->format('Y-m-27'));
    echo PHP_EOL;
}

以下実行結果

string(35) "2018-03-31 00:00 → 2018-03-27"
string(35) "2018-05-01 00:00 → 2018-05-27"
string(35) "2018-05-31 00:00 → 2018-05-27"
string(35) "2018-07-01 00:00 → 2018-07-27"
string(35) "2018-07-31 00:00 → 2018-07-27"
string(35) "2018-08-31 00:00 → 2018-08-27"
string(35) "2018-10-01 00:00 → 2018-10-27"
string(35) "2018-10-31 00:00 → 2018-10-27"
string(35) "2018-12-01 00:00 → 2018-12-27"
string(35) "2018-12-31 00:00 → 2018-12-27"

https://3v4l.org/WfDnT

案の定、最初に書いた処理と同じような結果になりました。
(※4月や6月は30日までしかないので、1日分、次の月に+されてしまってます)

オーバーフローしないメソッドaddMonthsNoOverflow()

ならオーバーフローしない計算メソッドを使えばいいんではないか、ということで
CarbonのaddMonthsNoOverflow()メソッドの処理を見てみます。

Carbon/Carbon.php
    /**
     * Add months without overflowing to the instance. Positive $value
     * travels forward while negative $value travels into the past.
     *
     * @param int $value
     *
     * @return static
     */
    public function addMonthsNoOverflow($value)
    {
        $day = $this->day;
        $this->modify((int) $value.' month');
        if ($day !== $this->day) {
            $this->modify('last day of previous month');
        }
        return $this;
    }

結局はmodify()メソッドを使用してるみたいですが、'last day of previous month'(前の月の最後の日)と書かれてるので、オーバーフローした際は前月の最終日を返すようになっているようです。

参考:PHP: 相対的な書式 - Manual

正しい処理になるように修正

ということで、CarbonのaddMonthsNoOverflow()メソッドを用いて計算するように先ほどの処理を修正してみます。

use Carbon\Carbon;

$array = [];
$contractDate = new Carbon('2018-01-31');

// 5回払い
for ($i = 0; $i < 5; $i++) {

    if ((int)$contractDate->format('d') < 15) {
        // $array['date'] = (new Carbon($contractDate))->addMonth($i + 1)->format("Y-m-27");
        $array['date'] = (new Carbon($contractDate))->addMonthsNoOverflow($i + 1)->format("Y-m-27");
    } else {
        // $array['date'] = (new Carbon($contractDate))->addMonth($i + 2)->format("Y-m-27");
        $array['date'] = (new Carbon($contractDate))->addMonthsNoOverflow($i + 2)->format("Y-m-27");
    }

    $array['target_month'] = (new Carbon($array['date']))->format('Y-m');

    var_dump($i);
    var_dump($array);
}

以下、tinker(laravel用REPL)で実行した結果

int(0)
array(2) {
  ["date"]=>
  string(10) "2018-03-27"
  ["target_month"]=>
  string(7) "2018-03"
}
int(1)
array(2) {
  ["date"]=>
  string(10) "2018-04-27"
  ["target_month"]=>
  string(7) "2018-04"
}
int(2)
array(2) {
  ["date"]=>
  string(10) "2018-05-27"
  ["target_month"]=>
  string(7) "2018-05"
}
int(3)
array(2) {
  ["date"]=>
  string(10) "2018-06-27"
  ["target_month"]=>
  string(7) "2018-06"
}
int(4)
array(2) {
  ["date"]=>
  string(10) "2018-07-27"
  ["target_month"]=>
  string(7) "2018-07"
}
>>>

期待通りの処理になりました。

ということで特に理由がなければCarbonを用いて月の計算をする場合はaddMonthsNoOverflow()メソッドを使うようにしたほうがいいかなというお話でした。

おわり

  • DateTimeクラスの仕様には度々振り回されます・・・
  • というかドキュメントちゃんと読みましょう>自分

参考URL

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

Composer: どのパッケージが依存しているか調べる方法

Composerで、あるパッケージが、どのパッケージから依存されているかを調べる方法です。

composer why --tree パッケージ名

例えば、symfony/yamlがどのパッケージで使われているか調べるには、次のようにします。

composer why --tree symfony/yaml

image.png

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

AWSを使ってLAMP環境を構築する手順

自己学習のメモとして。
AWS上にLAMP環境を作成する手順を書いておきます。

Linux仮想マシン(EC2)を起動

1、AWSのマネジメントコンソールからEC2を選択。
※この段階でリージョンを日本にしておく

2、インスタンスの作成を選択
・AMAZON Lunux AMIを選択
・無料利用枠のt2.microを選択
→インスタンスを作成

キーペアが生成できるのでダウンロードしておく。

パブリックIPアドレスを設定

パブリックIPアドレスが毎回変化してしまうので固定する。

・左のメニューからElastic IPを選択
・新しいアドレスの割り当てをクリック
・出来上がったアドレスを選択し、アクション→アドレスの関連付け
・インスタンスをクリックし、先ほど設定したLinuxのIDが出るので選択して紐づけ
・インスタンスからセキュリティグループをクリッ
アクション→インバウンドルールの編集を選択
・ルールの追加→http→保存

リモートアクセス

・ターミナル(コマンドプロンプト)を起動
・.sshフォルダがあるか確認する。(無ければmkdirでユーザーディレクトリの中に作っておく)
・.sshの中に先ほどのKey.pemファイルを移動しておく
・以下のsshコマンドでAWSにアクセス

ssh -i ~/.ssh/xxx.pem ec2-user@xx.xxx.xxx.xxx

※デフォルトで名前はec2-userになっている
初回はyesを選択して接続

今後接続するときは毎回このコマンドを打つので覚えておきたい

exitで接続を終了できる

Apacheを導入

・Linuxをアプデしておく

sudo yum -y update

※-yをつけると確認画面なしにできる

・Apacheをインストールする

sudo yum -y install httpd

・Apacheを起動する

sudo service httpd start

・今後自動で立ち上がるように設定する

sudo chkconfig httpd on

※ブラウザに切り替えてIPアドレスからapacheが見られるか確認するとよい

おまけ1

htmlファイルを作って試しに見てみる方法

htmlを作成

sudo vi /var/www/html/index.html

1、編集画面になったら「i」を打って適当にhtmlファイルを作成する
2、「esc」を押してから「:wq」で上書き保存しenter

※:q!で保存せず終了できる

ファイルの確認方法

ls /var/www/html

ブラウザにIPアドレスを打ち込んで確認できる

おまけ2

scpコマンドで、ローカルで作成したhtmlファイルを転送する
※scpはPC間で安全に転送するコマンド

おまけ1でAWSに作ったhtmlファイルをローカルにダウンロードする(デスクトップの場合)

scp -i ~/.ssh/xxx.pem ec2-user@xx.xxx.xxx.xxx:/var/www/html/index.html ~/Desktop

適当にhtmlファイルを編集し、AWSに転送する

scp -i ~/.ssh/xxx.pem ~/Desktop/index.html ec2-user@xx.xxx.xxx.xxx:

成功したかどうかsshコマンドで接続して確認
(pwd、lsで見られる。)

現状htmlファイルはec2-userの中にあるので正しい保存先に移動させる

sudo mv ~/index.html /var/www/html

これでブラウザで見ることができるようになる

この流れは面倒なのでもう少し簡略化できるよう設定する

EC2にリモートアクセスしている状態でwwwというグループを作成する

sudo groupadd www

このグループに自分のユーザー名を追加する

sudo usermod -a -G www ec2-user

いったんexitでログアウトし、もう一度ログイン→グループの権限が有効になっているはず

groups

で確認すると

ec2-user wheel www

となっているはず

htmlを保存するディレクトリのグループ所有権を変更する

sudo chown -R root:www /var/www

/var/www以下のディレクトリに書き込み許可を設定する

sudo chmod 2775 /var/www

将来のサブディレクトリにグループIDを設定

find /var/www -type d -exec sudo chmod 2775 {} \;

/var/www及びサブディレクトリのファイル許可を繰り返し、グループの書き込み許可を与える

find /var/www -type f -exec sudo chmod 0664 {} \;

今後は転送先に直接/var/www/htmlを指定できる

scp -i ~/.ssh/xxx.pem ~/Desktop/index.html ec2-user@xx.xxx.xxx.xxx:/var/www/html

PHPをインストール

※お決まりのコマンドでリモートログインしておく

管理者権限に切り替えておく

sudo su

ユーザー名の部分に#がつけばOK。
切り替えておけばsudoコマンドを打つ必要が無くなる。

時間の設定

date

ロンドンの時間になっていたりすると、掲示板などのサービスを作った際におかしくなるので日本時間に修正

ln -sf /usr/share/zoneinfo/Japan /etc/localtime

dateコマンドで確認して問題なければOK

・PHPをインストールする

yum install -y php

動作確認

・PHPの設定をしておく
※バックアップを取っておくと良い

cp /etc/php.ini /etc/php.bak

もしバックアップの必要が出た場合は

cp /etc/php.bak /etc/php.ini

viでphp.iniの設定をいじる

vi /etc/php.ini

行番号を表示させると見やすくなるのでescキーを押して

:set number

520行目あたりを編集したいので

:520

iモードにして

error_reporting = E_ALL &E_DEPRECATED 

に追記をすれば、phpの余分なエラーメッセージを表示させずに済む

error_reporting = E_ALL &E_DEPRECATED & ~E_NOTICE

次に537行目を編集。
ディスプレイエラーをOnにしておく
これでphpのエラーがブラウザに表示されるようになる

display_errors = On

ターミナルに戻り、apacheを再起動

service httpd restart

viでindex.phpを作って動作確認をすると良い
ブラウザでxx.xxx.xxx.xxx/index.php、(または番号のみで閲覧できる?)で確認できる。

MySQLのインストール

※sshでリモートアクセスし、sudo suで管理者にしておく。

yumでmysqlをインストールする

yum install -y mysql-server

phpからmysqlをコントロールするため追加プログラムを入れる

yum install -y php-mysqlnd

mysqlを起動してみる

service mysqld start

・mysqlの設定

mysql_secure_installation

パスワードを聞かれるが無いのでそのままenter
すると新しくパスワードを設定するかを聞かれるのでyを押し、パスワードを設定する
※パスワードは忘れないように
確認のためにもう一度打ち込む
そしてRemove anonyemous users?(ゲストユーザーを消しますか?)と尋ねられるのでyを押す。
そのあとはすべてyでOK。

・mysqlの文字コードをviで設定

vi /etc/my.cnf

11行目あたりにある[mysqld_safe]の前に

character-set-server = utf8

を追加。:wqで保存
再起動して確認

service mysqld restart

OKと表示されているればOK。

phpMyAdminを導入

phpMyAdminをインストールするためにyumコマンドの設定を変更する

yum-config-manager --enable epel

これで準備ができたので、phpmyadminをインストールする

yum install -y phpmyadmin

・自分のローカルPCのみでphpmyadminにログインできるよう設定する
自分のPCのグローバルIPアドレスを確認する
確認くん

確認できたらviで設定をいじる

vi /etc/httpd/conf.d/phpMyAdmin.conf

※大文字に注意

127.0.0.1の部分をグローバルIPアドレスに書き換えればOK

せっかくなので置換機能を使ってみる

:%s/127.0.0.1/xxx.xx.xx.xx/g

全4か所が修正されていればOK
設定を反映させるために再起動する

service httpd restart

確認のためxx.xx.xx.xx/phpmyadminをブラウザで確認する
ユーザー名はroot、パスワードはmysqlで設定したものを入力。

これにてLAMP環境が完成!

今後はLaravel環境を作る方法を書けるようになれればと思っています!

ありがとうございました。

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

Laravelリンク集

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

WordPressのテーマに登録されている有効なショートコード一覧の取得

調べる機会があったので。
下記のコードを適当なテーマに貼り付けて表示を確認してみてください。

ショートコード一覧の取得

$shortcode_tagsのkeyがショートコード名になっています。

<?php
    global $shortcode_tags;
    echo "<pre>";
    foreach ($shortcode_tags as $key => $value) {
        var_dump($key);
    }
    echo "</pre>";
    ?>

参考

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

php-master-changes 2019-04-14

今日は不要な条件コンパイルを削除する修正があった!

2019-04-14

carusogabriel: Remove HAVE_* for always available extensions

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