20200709のPHPに関する記事は16件です。

【PHP D問題編】Paizaスキルチェックで覚えておくと便利な関数を初学者がまとめてみた

PaizaのスキルチェックのD問題でよく使う、覚えておいた方がいいかもと個人的に関数をまとめてみました。
Webアプリをローカル環境でしか作れない初学者が書いた備忘録程度のものになりますので、そこまで高度なことは
書いておりません。むしろ基礎的な内容です。
最近始めたよ!みたいな方に少しでも役立てたら幸いです。

explode

文字列を区切って配列化する関数。
まず、以下のような文字列があったとします。

$input_line = '10 20 30 40'; 

このままでは、1行の文字列として扱われてしまうので各々の数字を扱うことができません。
そこで活躍するのがexplode関数です。新しく変数を定義し、第一引数に区切る境目を、第二引数には文字列が定義されている変数を持ってきます。つまり、どこで区切って、どの文字列を分けるのかを明記してあげます。

$input = explode(" ", $input_line);

この場合は、半角スペースで区切り、$input_lineの文字列を区切る。のようになっています。
また、この$inputは配列化されるので上記のコードは以下のコードと同じ意味を持つことになります。

$input = array(10, 20, 30, 40);

当然配列なのでそれぞれの数字にインデックス番号が割りふられ、ここでようやく各々の数字を扱うことができるようになるます。

30を扱いたい時

$input[2]

を使えば良い。

paizaのスキルチェックD問題ではこのように、問題で使う値が文字列で与えられることが多く、explodeを知らなければその先に進めないものもあります。

IMG_0072.jpeg
※このようなイメージを持っておくと良いかもしれない。

算術演算子

基礎中の基礎ですが、これも知らないと解けない問題があります。基本的な算術演算子は以下のとおりです。

足し算:$a + $b
引き算:$a - $b
掛け算:$a * $b
割り算:$a / $b
割り算のあまり: $a % $b

足し算、引き算は直感的にわかりますが、掛け算以降はコードを書くようになってから知りました。それくらい普段からあまりなじみのない記号を使い、基本的には他の言語でも変わらないみたいのでこれはマストで覚えるべきものだと思います。

比較演算子、論理演算子

比較演算子

比較演算子とは、その名の通りある二つの変数をを比較するための演算子です。
主な演算子は以下のようです。

$a == $b:$aと$bが等しい時にtrue
$a != $b:$aと$bが等しくない時にtrue
$a < $b :$bの方が$aよりも大きい時にtrue
$a > $b :$aの方が$bよりも大きい時にtrue
$a <= $b:$bの方が大きいまたは等しい時にtrue
$a >= $b:$aの方が大きいまたは等しい時にtrue

基本的に比較演算子も他の言語でもあまり変わらないことが多いようです。比較演算子は主にif文の中で使うことが多いです。

論理演算子

論理演算子も比較演算子と似ていてある二つの変数の関係性について調べる演算子です。

$a && $b:$aかつ$bのどちらの条件を満たしている時にtrue
$a || $b:$aまたは$bのどちらかが条件を満たしている時にtrue

まずはこの二つを覚えておけば少なくともスキルチェックのD問題は解くことができると思います。この論理演算子も比較演算子のようにif文で使うことがほとんどです。

floor関数

floor関数とは数値の小数部分を切り捨てる関数です。つまり以下のようになります。

echo floor(3.14);
//3と表示される

echo floor(233.1234)
//233と表示される

時々、小数点を切り捨てが必要になってくる問題もあるので覚えておくといいかもしれません。

substr関数

最後にsubstr関数です。これは文字数を制限する関数です。表示したい文字列を選択しすることができます。主な使い方は以下のようのです。

echo substr(‘abcdef’ ,1,3);
// bcdと表示される
echo substr(‘abcdef’ , 0,4);
//abcdeと表示される

最初の1文字目を0として表示したい区間の最初の文字の番号と最後の文字の番号を指定します。配列のインデックス番号のように一番最初が0であることに注意が必要です。
また、以下のように番号を負の値で指定することもできるみたいです。

echo substr(‘abcdef’ ,-1);
//fと表示される

実際負の数字で指定するよりも、扱う文字が相当長くない限り普通に数えて正の数で指定した方が楽なので実際に使ったことはないです。
以上が僕がpaizaのスキルチェックのD問題を解いていて身につけると便利になると感じた関数たちでした。
拙い記事でしたが、少しでもお役に立てれば幸いです。

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

DockerでPHP Extensionを入れるのをもう少し簡単にする

PHPのイメージを使っていて拡張を入れるときはdocker-php-ext-installを使ったりするけど、依存してるものを取ってくるのが少しめんどくさいと思ったりする。公式にも書いてあるけど、ちょっと試すだけなら以下の方法でもいいかもしれない。

Dockerfile

FROM php:7.4-apache

COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/bin/
RUN install-php-extensions bz2 curl fileinfo gd gettext intl mbstring exif mysqli pdo_mysql pdo_sqlite openssl ftp xdebug

install-php-extensionsが依存関係を解決してくれるみたい。
少しは楽になったかな?

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

DockerでPHP Extensionを簡単に入れる

PHPのイメージを使っていて拡張を入れるときはdocker-php-ext-installを使ったりするけど、依存してるものを取ってくるのが少しめんどくさいと思ったりする。公式にも書いてあるけど、ちょっと試すだけなら以下の方法でもいいかもしれない。

Dockerfile

FROM php:7.4-apache

COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/bin/
RUN install-php-extensions bz2 curl fileinfo gd gettext intl mbstring exif mysqli pdo_mysql pdo_sqlite openssl ftp xdebug

install-php-extensionsが依存関係を解決してくれるみたい。
少しは楽になったかな?

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

【PHP】コミット時に自動でコード整形、静的解析する

目的

git commit 時に自動でコード整形、静的解析をさせる。

使用ツール

コード整形:PHP-CS-Fixer
コード整形ルール:php-cs-fixer-rules
コード静的解析:phpstan
コード静的解析:phpmd

やってみる

ツールのインストール

composer require --dev friendsofphp/php-cs-fixer
composer require --dev suin/php-cs-fixer-rules
composer require --dev phpstan/phpstan
composer require --dev phpmd/phpmd
composer update

コード整形設定ファイルを書く

vi .php_cs.dist
<?php
    return PhpCsFixer\Config::create()
        ->setRiskyAllowed(true)
        ->setRules(Suin\PhpCsFixer\Rules::create([
            'declare_strict_types' => false,
        ]))
        ->setFinder(PhpCsFixer\Finder::create()
        ->exclude('vendor')
        ->in(__DIR__)
    );

コード静的解析設定ファイルを書く

vi phpmd_ruleset.xml
<?xml version="1.0"?>
    <ruleset
        name="My first PHPMD rule set"
        xmlns="http://pmd.sf.net/ruleset/1.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
        xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd"
    >
        <description>
            My custom rule set that checks my code...
        </description>

        <rule ref="rulesets/codesize.xml" />
        <rule ref="rulesets/cleancode.xml" />
        <rule ref="rulesets/controversial.xml" />
        <rule ref="rulesets/design.xml" />
        <rule ref="rulesets/naming.xml" />
        <rule ref="rulesets/unusedcode.xml" />
    </ruleset>

コミット時に上記ツールを自動で呼び出すよう設定

vi .git/hooks/pre-commit
if git rev-parse --verify HEAD >/dev/null 2>&1
then
    against=HEAD
else
    # Initial commit: diff against an empty tree object
    against=$(git hash-object -t tree /dev/null)
fi

# Redirect output to stderr.
exec 1>&2

IS_ERROR=0

for FILE in `git diff-index --name-status $against -- | grep -E '^[AUM].*\.php$'| cut -c3-`; do
    if php -l $FILE; then
        # PHPコード整形
        vendor/bin/php-cs-fixer fix $FILE

        # PHPコード静的解析
        if ! vendor/bin/phpstan analyse $FILE; then
            IS_ERROR=1
        fi

        # PHPコード静的解析
        if ! vendor/bin/phpmd $FILE text phpmd_ruleset.xml; then
            IS_ERROR=1
        fi
    else
        IS_ERROR=1
    fi
done

exit $IS_ERROR

おしまい

git commit 時に自動でコード整形、静的解析を行うようになりました。
行いたくない場合は git commit に --no-verify をつければOK。

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

【Laravel】Bladeの構文

書籍のアウトプットとして

Bladeの構文

レイアウト内でヒョ持したり、レイアウトを継承して複数を組み合わせたりするための構文について。

値の表示

{{変数関数等}}

{{}}はHTMLスケープ処理されるためHTMLタグとしては機能しない。
エスケープ処理してほしくない場合は以下のようにする。

{{!! 変数関数等 !!}}

@ifディレクティブ

Bladeにはディレクティブという言語で言う構文が用意されている。
まずは条件分岐(if)に相当するディレクティブから。

条件がtrueの時に表示する。

@if (条件)
...出力内容...
@endif

条件によって異なる表示をする

@if (条件)
...出力内容...
@else
...出力内容...
@endif

複数の条件を設定

@if (条件)
...出力内容...
@endif(条件) 
...出力内容...
@else
...出力内容...
@endif(条件) 

@ifには@else@elseifが用意されている

ディレクティブは何かを実行するのではなく表示する
条件がこれなら表示する、表示しないという動きをする。

@ifを使ってみる

以下のように修正する

index.blade.php
  <body>
    <h1>Blade/Index</h1>
    @if ($msg!='')
    <p>こんにちは、{{$msg}}さん。</p>
    @else
    <p>なにか書いてください</p>
    @endif
    <form action="/hello" method="post">
      @csrf
      <input type="text" name="msg">
      <input type="submit">
    </form>
  </body>

次はコントローラを修正

    public function index()
    {
        return view('hello.index', ['msg'=>'']);
    }
    public function post(Request $request)
    {
      return view('hello.index', ['msg'=>$request->msg]);
    }

/helloにアクセスして正常に表示されているか確認する。

特殊なディレクティブ

条件が非成立のときに表示

@unless(条件)
...表示内容...
@endunless

条件がfalseの時に表示をする。

変数が空の場合に表示

@empty(変数)
...表示内容...
@endempty

変数が定義済みの場合に表示

@isset(変数)
...表示内容...
@endisset

emptyと似ているが、これは変数そのものが定義されているかどうかを判定する。

@issetで変数定義をチェックする

@issetを使って例を見てみる。

index.blade.php
  <body>
    <h1>Blade/Index</h1>
    @isset ($msg)
    <p>こんにちは、{{$msg}}さん。</p>
    @else
    <p>なにか書いてください</p>
    @endisset
    <form action="/hello" method="post">
      @csrf
      <input type="text" name="msg">
      <input type="submit">
    </form>
  </body>

コントローラはこのように

    public function index()
    {
        return view('hello.index');
    }
    public function post(Request $request)
    {
      return view('hello.index', ['msg'=>$request->msg]);
    }

indexアクションメソッドでは値をテンプレに渡していない。
そのため、テンプレ側では$msgは未定義になる。

繰り返しのディレクティブ

forに相当

@for(初期化;条件;後処理)
...繰り返す表示...
#endfor

foreachに相当

@foreach($配列 as $変数)
...繰り返す表示...
@endforeach

foreach-elseに相当

@forelse($配列 as $変数)
...繰り返す表示...
@empty
...$変数が空のときの表示...
@endforelse

foreachにelseを追加したもの相当する。

whileに相当

@while(条件)
...繰り返す処理...
@endwhile

繰り返しディレクティブを利用する

index.blade.php
  <body>
    <h1>Blade/Index</h1>
    <p>&#064;foreachディレクティブの例</p>
    <ol>
      @foreach($data as $item)
      <li>{{$item}}</li>
      @endforeach
    </ol>
  </body>

コントローラ

    public function index()
    {
        $data=['one','two','three','four','five'];
        return view('hello.index', ['data'=>$data]);
    }

/helloにアクセスするとリストが表示される。
viewメソッドの[data->\$data]でdataに\$dataを設定してテンプレートに渡している。

$loopによるループ変数

$loopでループに関する情報が得られる。

プロパティ 説明
$loop->index 現在のインデックス(0から開始)
$loop->iteration 現在の繰り返しかず(1から開始)
$loop->remaining 後何回繰り返すか(残り回数)
$loop->count 繰り返しで使っているあ配列の要素数
$loop->first 最初の繰り返しかどうか(最初ならtrue)
$loop->last 最後の繰り返しかどうか(最後ならtrue)
$loop->depth 繰り返しのネスト数
$loop->parent ネストしている場合、親送り返しのループ変数を示す。

例を見てみる。

index.blade.php
  <body>
    <h1>Blade/Index</h1>
    <p>&#064;forディレクティブの例</p>
    @foreach($data as $item)
    @if($loop->first)
    <p>データ一覧</p><ul>
      @endif
      <li>No,{{$loop->iteration}}.{{$item}}</li>
      @if($loop->last)
    </ul><p>ーーーここまで</p>
    @endif
    @endforeach

  </body>

これで確認できる。

phpディテクティブについて

phpディレクティブでPHPスクリプトを直接実行できる。

@php
...PHPスクリプト...
@endphp
index.blade.php
  <body>
    <h1>Blade/Index</h1>
    <p>&#064;phpディレクティブの例</p>
    <ol>
      @php
      $counter=0;
      @endphp
      @while($counter<count($data))
      <li>{{$data[$counter]}}</li>
      @php
      $counter++;
      @endphp
      @endwhile
    </ol>
  </body>

テンプレに用意する@phpは最小限に、が基本

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

【徹底解説】初心者必見の関数(メソッド)の理解の仕方


元の投稿

https://wp.me/pakwHz-cr


プログラミングの入門をする上で最も大事と言っても過言ではない関数またはメソッド(以下、どちらも関数と呼びます)

皆さんは関数と聞くと何を思い浮かべますか?

一般的には、数学の授業で聞く言葉だと思います。

ではどうして、プログラミングの世界でメソッドの事を関数と呼ぶことになったのでしょうか?

それは数学の関数と概念が非常に似ているからです。

そこで、まずは数学的な関数を見てみましょう。数学アレルギーの人も拒絶せず難しい話はしませんので、読み進めてもらえると嬉しいです。

まず、関数とは何でしょう?

関数とはある任意のxを与えると必ず1つのy(解)を返してくれるものです。ちょっと言葉だけでは難しいですね。

それでは、数学的な関数とは代表的なものに何があるでしょうか?

私が一番はじめに思い浮かべる関数は

y = ax + b

です。これは一次関数と呼ばれるものですがaとbは定数(任意で数を決めれる)ので具体的に

y = 2x + 3

を見てみましょう。

この関数はxを指定するとある数字が答えを出してくれます。例えば、x = 3であれば yの値は9になります。これはいろんな数字に当てはめれますよね。

なので、関数とは特定のXを与えると1つのYを返してくれるものです。

image

この関数のようなものは数学に限られてるものではありません。

例えば、食材を与えると美味しい料理を作るシェフがいるとします。このシェフはある意味、関数のような働きをしていると言えましょう。

具体的にはこのシェフに「人参、じゃがいも、牛肉、カレー粉」をXとして、渡してあげると、このシェフはカレーというYの値を返してくれます。

image

このようなXを与えるとYを返すというのが、一般的な関数の概要です。

なので、プログラミング的な関数(メソッド)も同じだと言えます。

ただ、プログラミングでは上記のXを引数と呼び、Yを戻り値のといいます。

image

それでは実際のコードを見ていきましょう。

今回はRubyコードを使用していますが、主要なプログラミング言語ではこの概念は変わりません。

それでは与えられた2つの数字(引数)を足し算をするメソッドを作成しましょう

# メソッド名はadd
# 引数はa, b

# どんなメソッドを作るか、どんな引数を引き受けるかは自分で決めるので、
# メソッド名と引数の名前は自由に名前を決めることが出来ます。

# メソッドの定義(作成
def add(a, b)
  return a + b
end

# メソッドの呼び出し(使用)
add(3, 2)
# =>戻り値は5になる

このコードを図式化すると以下のようになります

ここで新しく紹介した概念が

「呼び出しのコードそのものが戻り値に変わる」

ということです。これは非常に大事な概念なのでしっかりと理解してください。

つまり、

result = add(2, 3)

というコードは

result = 5

というコードとしているのと全く同じです。

ここで、どうして 5 と直接書くのではなく add(2,3) と書くのかというといくつか理由があります

まず、この例題では足し算をするメソッドを使用しているので

頭の中で5とすぐに戻り値を予想できますがこれがすごく複雑な処理だと

戻り値が予想できないのでメソッドを使用しないといけません。

次に、今回の場合では引数(a, b)が予め決められていますが

これは実際には変数になっていたりしますのでメソッドを使用する前に答えを予想することができなくなります。

ここまでで、メソッドの基本的な概念は終わりです。

プログラミングではたくさんの関数(メソッド)を使用します。

書いているもののほとんどが関数と言っても過言ではありません。

関数を定義することも多々ありますが、大きくは他人が作成した関数をしようするということがほとんどです。

その際に、引数として何が必要でどのような戻り値をくれるのかという概念で関数をみると

簡単に理解して関数の詳細を理解しなくても使用することが出来ます。

なので、関数(メソッド)を使用または定義する際は引数と戻り値に注目してみましょう。 

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

Slim3 Framework×slim-skeleton不使用×twigでプロジェクトを作成する(3. LoggingとController)

はじめに

Slim3 Framework×slim-skeleton不使用×twigでプロジェクトを作成する
(2. DocumentRoot変更~Twigを使用)
の続きです。

前提

下記記事で構築した環境を前提とします。

  • Windows10にVagrantをを入れてCentOS7をインストールしよう(123456)
  • ローカルでLAMP環境を構築しよう(01234
  • CentOS7にComposerをインストールしよう

  • 私家版 Slim Framework チュートリアル (123456)
    @nunulkさんのチュートリアルを一通り。
    ここで作成したDBを使いまわします。

  • Slim3 Framework×slim-skeleton不使用×twigでプロジェクトを作成する(12

手順

1. 専用のユーザーを作成
2. プロジェクトディレクトリを作成・slim3インストール
3. 各種設定
4. DocumentRootを変更&表示確認
5. Twigを使ってみよう
6. Loggingしてみよう
7. Controllerを作成しよう
8. PDOを使用してデータベースに接続しよう
9. @nunulkさんのチュートリアルで作成したチケット管理システムをtwigを使って再現しよう

やってみよう

今回の記事では、手順6~7を行います。

6. Loggingしてみよう

monologインストール

以下のコマンドでmonologをインストールします。

composer require monolog/monolog

settings.phpに設定を追加

config/settings.phpに下記設定を追加します。
ダニエルさんのサイトの記述のままだと、エラーログの日付が下記みたいになってしまうので、
[2020-07-09T06:09:09.183042+00:00] app.ERROR: My error message! [] []
日付と出力形式を追加します。

config/settings.php
$settings['logger'] = [
  'name'        => 'app',
  'file'        => $settings['temp'] . '/logs/app.log',
  'level'       => \Monolog\Logger::ERROR,
  'date_format' => 'Y-m-d H:i:s',
  'output'      => "[%datetime%] %level_name%: %message% %context% %extra%\n",
];

container.phpに設定を追加

config/container.phpに下記設定を追加します。
こちらもconfig/settings.phpに追加した日付と出力形式を有効にするための記載が追加されています。

config/container.php
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Logger;
use Psr\Log\LoggerInterface;

$container[LoggerInterface::class] = function (Container $container) {
    $settings = $container->get('settings')['logger'];
    $level = isset($settings['level']) ? $settings['level'] : Logger::ERROR;
    $logFile = $settings['file'];
    $date_format = $settings['date_format'];
    $output = $settings['output'];
    $formatter = new LineFormatter($output, $date_format);

    $logger = new Logger($settings['name']);
    $handler = new RotatingFileHandler($logFile, 0, $level, true, 0775);
    $handler->setFormatter($formatter);
    $logger->pushHandler($handler);

    return $logger;
};

routes.phpにルートを追加

config/routes.phpに下記ルートを追加します。

config/routes.php
use Psr\Log\LoggerInterface;

$app->get('/logger-test', function (Request $request, Response $response) {
    /** @var Container $this */
    /** @var LoggerInterface $logger */

    $logger = $this->get(LoggerInterface::class);
    $logger->error('My error message!');

    $response->getBody()->write("Success");

    return $response;
});

動作確認

ブラウザで以下のURLを開いてください。以下が確認できればOKです。
1. ブラウザにSuccessと表示される。
2. ログファイルtmp/logs/app-2020-07-09.logが生成される。
3. tmp/logs/app-2020-07-09.logに下記例のようなエラーログが記載されている。
例)[2020-07-09 06:42:56] ERROR: My error message! [] []

http://192.168.33.90/logger-test

7. Controllerを作成しよう

リクエストとレスポンスを処理するためのパブリックメソッドを1つだけ与えるSingle Action Controllersと
依存性の注入を行うDependency injectionの2パターンのControllerクラスを作成します。
ダニエルさんはControllerクラスといいながらクラスの命名を××Actionクラスとしているので、そこは踏襲します。

Single Action Controllers

PingAction.php作成

srcディレクトリの下にActionディレクトリを作成し、PingAction.phpというファイルを作成します。
PingAction.phpには以下のように記述します。

src/Action/PingAction.php
<?php

namespace App\Action;

use Psr\Http\Message\ResponseInterface;
use Slim\Http\Request;
use Slim\Http\Response;

final class PingAction
{
    public function __invoke(Request $request, Response $response): ResponseInterface
    {
        return $response->withJson(['success' => true]);
    }
}

routes.phpにルートを追加

config/routes.php
$app->any('/ping', \App\Action\PingAction::class);

動作確認

ブラウザで以下のURLを開き、{"success":true}と表示されればOKです。

Dependency injection

HomeIndexAction.php作成

src/Action/HomeIndexAction.phpというファイルを作成し、以下のように記述します。
ダニエルさんはActionInterfaceクラスをimplementするようなことを書いてあったのですが、
implementすると動かないので省きました。

src/Action/HomeIndexAction.php
<?php

namespace App\Action;

use Psr\Http\Message\ResponseInterface;
use Slim\Http\Request;
use Slim\Http\Response;
use Slim\Views\Twig;

final class HomeIndexAction
{
    /**
     * @var Twig
     */
    private $twig;

    /**
     * Constructor.
     *
     * @param Twig $twig
     */
    public function __construct(Twig $twig)
    {
        $this->twig = $twig;
    }

    /**
     * Action.
     *
     * @param Request $request the request
     * @param Response $response the response
     *
     * @return ResponseInterface the response
     */
    public function __invoke(Request $request, Response $response): ResponseInterface
    {
        $viewData = [
            'name' => 'World',
        ];

        return $this->twig->render($response, 'Home/home-index.twig', $viewData);
    }
}

container.phpに設定を追加

config/container.phpに下記設定を追加します。

config/container.php
$container[\App\Action\HomeIndexAction::class] = function (Container $container) {
    $twig = $container->get(\Slim\Views\Twig::class);
    return new \App\Action\HomeIndexAction($twig);
};

routes.phpにルートを追加

config/routes.php
$app->get('/home', \App\Action\HomeIndexAction::class);

home-index.twigを作成

templateディレクトリの下にHomeディレクトリを作成し、その下にhome-index.twigを作成します。
template/Home/home-index.twigには以下のように記述します。

template/Home/home-index.twig
Hello {{ name }}!

動作確認

ブラウザで以下のURLを開き、Hello World!と表示されればOKです。

http://192.168.33.90/home

参考サイト

Creating your first Slim 3 Framework Application
PHP monologをカスタマイズしてみた

関連ページ

Windows10にVagrantをを入れてCentOS7をインストールしよう

1. VagrantインストールからVagrantfileを設置まで
2. 仮想マシンの操作
3. WinSCP、Tera Termに秘密鍵でログイン
4. WinSCP、Tera Termにrootユーザーでパスワードログイン
5. zip/unzipをインストール
6. Vagrantにて仮想環境を配布

ローカルでLAMP環境を構築しよう

0. 事前準備
1. Apacheをインストール
2. MySQLをインストール
3. PHPをインストール
4. ファイアウォールとか停止する

Composerをインストール

CentOS7にComposerをインストールしよう

PHP Slim3フレームワークのサンプルアプリを作ろう

2-1. First Application Walkthrough Getting Set Upまで

Apache

DocumentRootを変更しよう

Slim3 Framework×slim-skeleton不使用×twigでプロジェクトを作成する

1. プロジェクト作成~各種設定
2. DocumentRoot変更~Twigを使用
3. LoggingとController
4.PDO使用

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

CakePHP覚え書き

何の記事?

CakePHPのチュートリアルをやっていて分からなくて検索した単語を羅列する
※調べる度に追記していく

【CakePHP公式チュートリアル】
https://book.cakephp.org/3/ja/tutorials-and-examples.html

覚え書き

Component

・コントローラから呼べる共通メソッド
・デフォルトでよく使うやつは用意されている
 ・認証(AuthComponent)
 ・クッキー(CookieComponent)
 ・クロスサイトリクエストフォージェリ(CsrfComponent)
 ・フラッシュ(FlashComponent)
 ・セキュリティ(SecurityComponent)
 ・ページネーション(PaginatorComponent)
 ・リクエストハンドリング(RequestHandlerComponent)
・オリジナルは「src/Controller/Component/」にファイルを作る必要がある
・useで継承する必要がある
・使い方

#①
public $components = ['コンポーネント名'];

#②
$now = $this->Date->today();

#③
$this->loadComponent('コンポーネント名');

【参考】
https://www.sejuku.net/blog/30423

AuthComponent

・ログインやログアウト、ユーザ情報の提供などをしてくれる
・AppController.phpのinitializeメソッドでその設定および処理が行われている
・チュートリアルではUsersController.phpでログイン機能を追加した

【チュートリアルででてきたところ】
https://book.cakephp.org/3/ja/tutorials-and-examples/bookmarks/part-two.html

AppController.php
        $this->loadComponent('Auth', [
            'authenticate' => [
                'Form' => [
                    'fields' => [
                        'username' => 'email',
                        'password' => 'password'
                    ]
                ]
            ],
            'loginAction' => [
                'controller' => 'Users',
                'action' => 'login'
            ],
            'unauthorizedRedirect' => $this->referer() // 未認証時、元のページを返します。
        ]);

        // PagesController が動作し続けるように
        // display アクションを許可
        $this->Auth->allow(['display']);
UsersController.php
public function login()
{
    if ($this->request->is('post')) {
        $user = $this->Auth->identify();
        if ($user) {
            $this->Auth->setUser($user);
            return $this->redirect($this->Auth->redirectUrl());
        }
        $this->Flash->error('あなたのユーザー名またはパスワードが不正です。');
    }
}

AuthComponent::identify()

identify()メソッドをもちいてリクエスト中の認証情報を使用してユーザーを識別する

 $this->Auth->identify() 

setUser()メソッドをもちいてセッションにユーザー情報を保存する、すなわち、ユーザーをログインする

$this->Auth->setUser()

'authorize'=> 'Controller'

【チュートリアルででてくるところ】
https://book.cakephp.org/3/ja/tutorials-and-examples/bookmarks/part-two.html#id5

ブックマークのアクセスを制限するためになんかいろいろやっている

AppController.php
#initialize()メソッドに設定の追加
'authorize'=> 'Controller',//この行を追加

#isAuthorizedメソッドの追加
public function isAuthorized($user)
{
    return false;
}

BookmarkControllerにもisAuthorized()メソッドの追加

BookmarkController.php
public function isAuthorized($user)
{
    $action = $this->request->getParam('action');

    // add と index アクションは常に許可します。
    if (in_array($action, ['index', 'add', 'tags'])) {
        return true;
    }
    // その他のすべてのアクションは、id を必要とします。
    if (!$this->request->getParam('pass.0')) {
        return false;
    }

    // ブックマークが現在のユーザーに属するかどうかをチェック
    $id = $this->request->getParam('pass.0');
    $bookmark = $this->Bookmarks->get($id);
    if ($bookmark->user_id == $user['id']) {
        return true;
    }
    return parent::isAuthorized($user);
}

AppControllerではisAuthorized()が必ずfalseを返して
BookmarkControllerでは場合によってはtrueにしているよう

ここに書いてあった
https://book.cakephp.org/3/ja/controllers/components/authentication.html#controllerauthorize

isAuthorized()メソッドでtrueになった場合だけ該当ユーザーがリクエスト内で リソースにアクセスすることが許可される、みたい

【参考】
https://qiita.com/kazu56/items/a54596e963d9e2b71f2e
https://www.sejuku.net/blog/30688
https://book.cakephp.org/3/ja/controllers/components/authentication.html
https://book.cakephp.org/3/ja/controllers/components/authentication.html#controllerauthorize

AppController.php

・アプリケーションのすべてのコントローラーの親クラス
・アプリケーションのコントローラー全体で共有されるメソッドを入れるようにする

【参考】
https://book.cakephp.org/3/ja/controllers.html

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

【PHP】祝日取得クラス

日本の祝日を返すクラスです。
Google Calendar APIを利用する方法をはじめ同様の処理は既にいくつも出回っていますが…

以前より個人的に使っていた処理がクラス化していなかった上、祝日定義の記述自体にも多重に三項演算子を多用していて少々メンテナンスしにくかったので、クラス化のついでにその辺も作り直しました。

春分の日及び秋分の日については簡易的な実装ですので、現在から遠い年では正しい値を返す保証はありません。

holiday_class.php
<?php
class Holiday {
    private $holidayDefinitions;
    private $year;
    private $result;
    private $useIndefiniteHoliday;
    private $resultType;

    public function __construct($year = 0) {
        if($year < 1) $year = date('Y');
        $this->year = (int) $year;

        // 祝日定義
        // ('国民の祝日に関する法律'が公布・施行された1948年7月20日以降のもののみ)
        $this->holidayDefinitions = [
            1 => [
                '1949:1'    => '元日',
                '1949:15, 2000:2_1'
                            => '成人の日',
            ],
            2 => [
                '1967:11'   => '建国記念の日',
                '2020:23'   => '天皇誕生日',
                '1989:24, 1990:0'
                            => '大喪の礼',
            ],
            3 => [
                '1949:s'    => '春分の日',
            ],
            4 => [
                '1959:10, 1960:0'
                            => '結婚の儀',
                '1949:29'   => '天皇誕生日, 1989:みどりの日, 2007:昭和の日',
            ],
            5 => [
                '2019:1, 2020:0'
                            => '皇太子殿下即位・改元',
                '1949:3'    => '憲法記念日',
                '2007:4'    => 'みどりの日',
                '1949:5'    => 'こどもの日',
            ],
            6 => [
                '1993:9, 1994:0'
                            => '結婚の儀',
            ],
            7 => [
                '1996:20, 2003:3_1, 2020:23, 2021:3_1'
                            => '海の日',
                '2020:24, 2021:0'
                            => 'スポーツの日',
            ],
            8 => [
                '2016:11, 2020:10, 2021:11'
                            => '山の日',
            ],
            9 => [
                '1966:15, 2003:3_1'
                            => '敬老の日',
                '1948:a'    => '秋分の日',
            ],
            10 => [
                '1966:10, 2000:2_1, 2020:0, 2021:2_1'
                            => '体育の日, 2020:スポーツの日',
                '2019:22, 2020:0'
                            => '即位礼正殿の儀',
            ],
            11 => [
                '1948:3'    => '文化の日',
                '1990:12, 1991:0'
                            => '即位礼正殿の儀',
                '1948:23'   => '勤労感謝の日',
            ],
            12 => [
                '1989:23, 2019:0'
                            => '天皇誕生日',
            ],
        ];

        $this->result = [];
        $this->useIndefiniteHoliday = 1;
        $this->resultType = 0;
    }

    /**
     *  1年分のリストを返す
     */
    public function getHolidayOfYear($year = 0) {
        if($year < 1) $year = $this->year;
        $year = (int) $year;

        if(!isset($this->result[$year])) {
            // 該当年の祝日配列に変換
            $holiday = [];
            $equinox = .242194 * ($year - 1980) - floor(($year - 1980) / 4);
            foreach($this->holidayDefinitions as $month => $currentMonthData) {
                $holiday[$month] = [];
                foreach($currentMonthData as $days => $names) {
                    // 対象年・日取得
                    $days = explode(',', $days);
                    $arrTmp = [];
                    foreach($days as $tmp) {
                        $tmp = explode(':', $tmp);
                        $arrTmp[(int) $tmp[0]] = trim($tmp[1]);
                    }
                    $yearTmp = 0;
                    foreach(array_keys($arrTmp) as $tmp)
                        if($year >= $tmp && $tmp >= $yearTmp) $yearTmp = $tmp;

                    if($yearTmp == 0) continue;

                    // 日を記述形式ごとに取得
                    if($arrTmp[$yearTmp] === 's')
                        $day = floor(20.8431 + $equinox);
                    elseif($arrTmp[$yearTmp] === 'a')
                        $day = floor(23.2488 + $equinox);
                    elseif(strpos($arrTmp[$yearTmp], '_') !== false) {
                        [$num, $w] = explode('_', $arrTmp[$yearTmp]);
                        $day = $this->getDayOfNumWeek($year, $month, $num, $w);
                    }
                    else
                        $day = $arrTmp[$yearTmp];

                    if($day < 1) continue;

                    // 名称取得
                    $names = explode(',', $names);
                    $arrTmp = [];
                    foreach($names as $tmp) {
                        $tmp = explode(':', $tmp);
                        if(count($tmp) == 1)
                            $arrTmp[0] = trim($tmp[0]);
                        else
                            $arrTmp[(int) $tmp[0]] = trim($tmp[1]);
                    }

                    $yearTmp = 0;
                    foreach(array_keys($arrTmp) as $tmp)
                        if($year >= $tmp && $tmp >= $yearTmp) $yearTmp = $tmp;

                    $holiday[$month][$day] = !isset($holiday[$month][$day]) ?
                        $arrTmp[$yearTmp] : $holiday[$month][$day]. ', '. $arrTmp[$yearTmp];
                }
            }

            // 国民の休日・振替休日
            if($this->useIndefiniteHoliday)
                $holiday = $this->indefiniteHoliday($holiday, $year);

            foreach($holiday as $k => $v) ksort($holiday[$k]);
            $this->result[$year] = $holiday;
        }

        return $this->resultType ?
            $this->convertLinear($year, $this->result[$year]) :
            $this->result[$year];
    }

    /**
     *  国民の休日・振替休日
     */
    private function indefiniteHoliday($holiday, $year) {
        for($month = 1; $month <= 12; $month++) {
            // 月末日
            $lastDay = (int) date('d', strtotime("last day of $year-$month"));
            for($day = 1; $day <= $lastDay; $day++) {
                // 前日の月日
                $prevTime = strtotime("$year-$month-$day -1 day");
                $prevMonth = (int) date('m', $prevTime);
                $prevDay = (int) date('d', $prevTime);

                // 祝日に挟まれた平日を国民の休日に変更(1986年以降)
                if( $year >= 1986 &&
                    isset($holiday[$prevMonth][$prevDay])
                ) {
                    // 翌日の月日
                    $nextTime = strtotime("$year-$month-$day +1 day");
                    $nextMonth = (int) date('m', $nextTime);
                    $nextDay = (int) date('d', $nextTime);
                    if( isset($holiday[$nextMonth][$nextDay]) &&
                        !isset($holiday[$month][$day]) &&
                        date('w', strtotime("$year-$month-$day")) != 0
                    ) {
                        $holiday[$month][$day] = '国民の休日';
                    }
                }

                // 振替休日(1973年4月以降)
                if(($year == 1973 && $month >= 4 || $year > 1973) &&
                    // 祝日かつ日曜
                    isset($holiday[$month][$day]) &&
                    date('w', strtotime("$year-$month-$day")) == 0
                ) {
                    // その日以降の直近の平日を振替休日に
                    for($i = 1; $i < 7; $i++) {
                        $t = strtotime("$year-$month-$day +{$i} day");
                        $m = (int) date('m', $t);
                        $d = (int) date('d', $t);
                        if(!isset($holiday[$m][$d])) {
                            $holiday[$m][$d] = '振替休日';
                            break;
                        }
                    }
                }
            }
        }
        return $holiday;
    }

    /**
     *  $year年 $month月 第$num $w曜日に該当する日を返す
     */
    private function getDayOfNumWeek($year, $month, $num, $w) {
        $firstDayWeek = (int) date('w', strtotime("$year-$month-01"));
        return 1 + ($num - 1) * 7 + (7 + $w - $firstDayWeek) % 7;
    }

    /**
     *  YYYY-MM-DDをキーとした連想配列に変換
     */
    private function convertLinear($year, $array) {
        $arrTmp = [];
        foreach($array as $month => $currentMonthData) {
            foreach($currentMonthData as $days => $names) {
                $arrTmp[sprintf('%04d-%02d-%02d', $year, $month, $days)] = $names;
            }
        }
        return $arrTmp;
    }

    /**
     *  1か月分のリストを返す
     */
    public function getHolidayOfMonth($month = 0) {
        if($month < 1 || $month > 12) $month = date('m');
        $year = $this->year;

        // 該当年の結果が未取得であれば取得
        if(!isset($this->result[$year]))
            $this->getHolidayOfYear();

        if($this->resultType) {
            $arrTmp = [];
            foreach($this->result[$year][(int) $month] as $days => $names) {
                $arrTmp[sprintf('%04d-%02d-%02d', $year, $month, $days)] = $names;
            }
            return $arrTmp;
        } else {
            return $this->result[$year][(int) $month];
        }
    }

    /**
     *  国民の休日・振替休日 使用フラグ変更
     */
    public function setUseIndefiniteHoliday($flg = 1) {
        $this->useIndefiniteHoliday = $flg == 1 ? 1 : 0;

        // 取得済みの結果をリセット
        $this->result = [];
    }

    /**
     *  戻り値形式変更
     */
    public function setResultType($flg = 0) {
        $this->resultType = $flg == 0 ? 0 : 1;
    }

    /**
     *  年変更
     */
    public function setYear($year = 0) {
        if($year < 1) $year = date('Y');
        $this->year = (int) $year;
    }
}

使用例

インスタンス生成

new Holiday([$year])
year
年プロパティの初期値。省略、または0以下の値を渡した場合は現在の年で設定。

example
$holiday = new Holiday(2020);
$holiday = new Holiday();
$holiday = new Holiday;

メソッド

年プロパティ変更
setYear([$year])
year
。省略、または0以下の値を渡した場合は現在の年で設定。

example
$holiday = new Holiday;
$holiday->setYear(2000);
$holiday->setYear();

1年分の祝日リストを取得
getHolidayOfYear([$year])
year
。省略、または0以下の値を渡した場合は年プロパティの値を使用。
インスタンス内の年プロパティ変更はしません。

example
$holiday = new Holiday;
print_r($holiday->getHolidayOfYear());
result
Array
(
    [1] => Array
        (
            [1] => 元日
            [13] => 成人の日
        )

    [2] => Array
        (
            [11] => 建国記念の日
            [23] => 天皇誕生日
            [24] => 振替休日
        )

    [3] => Array
        (
            [20] => 春分の日
        )

    [4] => Array
        (
            [29] => 昭和の日
        )

    [5] => Array
        (
            [3] => 憲法記念日
            [4] => みどりの日
            [5] => こどもの日
            [6] => 振替休日
        )

    [6] => Array
        (
        )

    [7] => Array
        (
            [23] => 海の日
            [24] => スポーツの日
        )

    [8] => Array
        (
            [10] => 山の日
        )

    [9] => Array
        (
            [21] => 敬老の日
            [22] => 秋分の日
        )

    [10] => Array
        (
        )

    [11] => Array
        (
            [3] => 文化の日
            [23] => 勤労感謝の日
        )

    [12] => Array
        (
        )

)

1か月分の祝日リストを取得
getHolidayOfMonth([$month])
month
。省略時または範囲外の値を指定した場合は現在の月が使用されます。
年はインスタンス内の年プロパティを使用します。

example
$holiday = new Holiday;
print_r($holiday->getHolidayOfMonth());
result
Array
(
    [23] => 海の日
    [24] => スポーツの日
)

戻り値のリスト形式変更
setResultType([$resultType])
resultType
戻り値の形式
0: 月、日をキーとした連想配列(デフォルト)
1: YYYY-MM-DD をキーとした連想配列

example
$holiday = new Holiday;
$holiday->setResultType(1);
print_r($holiday->getHolidayOfYear());
print_r($holiday->getHolidayOfMonth(5));
result
Array
(
    [2020-01-01] => 元日
    [2020-01-13] => 成人の日
    [2020-02-11] => 建国記念の日
    [2020-02-23] => 天皇誕生日
    [2020-02-24] => 振替休日
    [2020-03-20] => 春分の日
    [2020-04-29] => 昭和の日
    [2020-05-03] => 憲法記念日
    [2020-05-04] => みどりの日
    [2020-05-05] => こどもの日
    [2020-05-06] => 振替休日
    [2020-07-23] => 海の日
    [2020-07-24] => スポーツの日
    [2020-08-10] => 山の日
    [2020-09-21] => 敬老の日
    [2020-09-22] => 秋分の日
    [2020-11-03] => 文化の日
    [2020-11-23] => 勤労感謝の日
)
Array
(
    [2020-05-03] => 憲法記念日
    [2020-05-04] => みどりの日
    [2020-05-05] => こどもの日
    [2020-05-06] => 振替休日
)

国民の休日及び振替休日の使用指定
setUseIndefiniteHoliday([$useIndefiniteHoliday])
useIndefiniteHoliday
0: 使用しない
1: 使用する(デフォルト)

example
$holiday = new Holiday(2019);
$holiday->setResultType(1);
print_r($holiday->getHolidayOfMonth(5));
$holiday->setUseIndefiniteHoliday(0);
print_r($holiday->getHolidayOfMonth(5));
result
Array
(
    [2019-05-01] => 皇太子殿下即位・改元
    [2019-05-02] => 国民の休日
    [2019-05-03] => 憲法記念日
    [2019-05-04] => みどりの日
    [2019-05-05] => こどもの日
    [2019-05-06] => 振替休日
)
Array
(
    [2019-05-01] => 皇太子殿下即位・改元
    [2019-05-03] => 憲法記念日
    [2019-05-04] => みどりの日
    [2019-05-05] => こどもの日
)

参考

「国民の祝日」について | 内閣府
昭和30年(1955年)から令和3年(2021年)国民の祝日 csv | 内閣府

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

Slim3 Framework×slim-skeleton不使用×twigでプロジェクトを作成する(2. DocumentRoot変更~Twigを使用)

はじめに

Slim3 Framework×slim-skeleton不使用×twigでプロジェクトを作成する
(1. プロジェクト作成~各種設定)
の続きです。

前提

下記記事で構築した環境を前提とします。

  • Windows10にVagrantをを入れてCentOS7をインストールしよう(123456)
  • ローカルでLAMP環境を構築しよう(01234
  • CentOS7にComposerをインストールしよう

  • 私家版 Slim Framework チュートリアル (123456)
    @nunulkさんのチュートリアルを一通り。
    ここで作成したDBを使いまわします。

  • Slim3 Framework×slim-skeleton不使用×twigでプロジェクトを作成する(1

手順

1. 専用のユーザーを作成
2. プロジェクトディレクトリを作成・slim3インストール
3. 各種設定
4. DocumentRootを変更&表示確認
5. Twigを使ってみよう
6. Loggingしてみよう
7. Controllerを作成しよう
8. PDOを使用してデータベースに接続しよう
9. @nunulkさんのチュートリアルで作成したチケット管理システムをtwigを使って再現しよう

やってみよう

今回の記事では、手順4~5を行います。

4. DocumentRootを変更&表示確認

userをrootに切り替えて行います。

設定ファイル/etc/httpd/conf/httpd.confの該当部分を下記のように書き換えます。

/etc/httpd/conf/httpd.conf
#
# DocumentRoot: The directory out of which you will serve your
# documents. By default, all requests are taken from this directory, but
# symbolic links and aliases may be used to point to other locations.
#
DocumentRoot "/home/slimuser/projects/slim/SampleApplication/public"

#
# Relax access to content within /var/www.
#
<Directory "/home/slimuser/projects/slim/SampleApplication/public">
    ##AllowOverride None
    AllowOverride All
    ##Allow open access:
    Require all granted
</Directory>

次に以下のコマンドでApacheを再起動します。

systemctl restart httpd

以下のコマンドでプロジェクトフォルダにパーミッション付与します。

chown apache:apache -R /home/slimuser/projects/SampleApplication/*
chmod 777 -R /home/slimuser/projects/SampleApplication/*

以下のURLを開いて、前回の記事で行った動作確認と同じ動作が確認出来たらOKです。

http://192.168.33.90/
http://192.168.33.90/hello/world

5. Twigを使ってみよう

Twigをインストール

以下のコマンドでtwigをインストールします。

composer require slim/twig-view 2.*

settings.phpに設定を追加

config/settings.phpに以下のように追記します。

config/settings.php
$settings['twig'] = [
  'path' => $settings['root'] . '/templates',
  'cache_enabled' => true,
  'cache_path' => $settings['temp'] . '/twig-cache'
];

container.phpに設定を追加

config/container.phpに以下のように追記します。
use ~;部分はこれからも増えますので、$container = $app->getContainer();の上にまとめて記述してください。

config/container.php
$container[Twig::class] = function (Container $container) {
  $settings = $container->get('settings');
  $viewPath = $settings['twig']['path'];

  $twig = new Twig($viewPath, [
    'cache' => $settings['twig']['cache_enabled'] ? $settings['twig']['cache_path'] : false
  ]);

  $loader = $twig->getLoader();
  $loader->addPath($settings['public'], 'public');

  $router = $container->get('router');
  $uri = \Slim\Http\Uri::createFromEnvironment($container->get('environment'));
  $twig->addExtension(new \Slim\Views\TwigExtension($router, $uri));

  return $twig;
};

twigファイルを作成

templates/time.twigを作成します。
現在時刻が表示されるだけの簡潔なものです。

templates/time.twig
<?DOCTYPE html>
<html>
  <head>
    <base href="{{ base_url() }}" />
  </head>
  <body>
    Current time:{{ now }}
  </body>
</html>

routes.phpにルートを追加

config/routes.phpに以下のように追記します。
use ~;部分はこれからも増えますので、上のほうにまとめて記述してください。

config/routes.php
use Slim\Http\Request;
use Slim\Http\Response;
use Slim\Views\Twig;

$app->get('/time', function (Request $request, Response $response) {
    $viewData = [
        'now' => date('Y-m-d H:i:s')
    ];

    return $this->get(Twig::class)->render($response, 'time.twig', $viewData);
});

動作確認

以下のURLを開いてCurrent time:2020-07-09 05:32:24のように現在時刻が表示されればOKです。

http://192.168.33.90/time

参考サイト

Creating your first Slim 3 Framework Application

関連ページ

Windows10にVagrantをを入れてCentOS7をインストールしよう

1. VagrantインストールからVagrantfileを設置まで
2. 仮想マシンの操作
3. WinSCP、Tera Termに秘密鍵でログイン
4. WinSCP、Tera Termにrootユーザーでパスワードログイン
5. zip/unzipをインストール
6. Vagrantにて仮想環境を配布

ローカルでLAMP環境を構築しよう

0. 事前準備
1. Apacheをインストール
2. MySQLをインストール
3. PHPをインストール
4. ファイアウォールとか停止する

Composerをインストール

CentOS7にComposerをインストールしよう

PHP Slim3フレームワークのサンプルアプリを作ろう

2-1. First Application Walkthrough Getting Set Upまで

Apache

DocumentRootを変更しよう

Slim3 Framework×slim-skeleton不使用×twigでプロジェクトを作成する

1. プロジェクト作成~各種設定
2. DocumentRoot変更~Twigを使用
3. LoggingとController
4.PDO使用

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

GuzzleのJSONでのPOSTに大変苦労したお話

who are you?

Guzzle使うから〜と聞いた時に本当に1mmも知りませんでした()

ギリギリcurlは触ったことはあるし、ただいつもAPIへ通信していたのが、
何故かajaxばかりの環境で育ってきた僕

育ってきた環境が違うから〜知らない言語はしょうがない〜

・・・(ー_ー)

それでもGuzzleを使うと言うことであれば、
やらなきゃならない事がある><

・・・

環境

PHP:7系
Guzzle:6系
Laravel:7系(例題はLaravelでの実装ですが他もほぼほぼ変わらないと思います)

Guzzleを使ったAPI通信

GuzzleSampleController.php
    $client = new Client();

    $options = [
        'header' => [
            'Authorization' => $token,
            'Content-Type' => 'application/json',
        ],
        'form_params' => [
            'id1' => $id1,
        ],
    ];

$response = $client->request('POST', $url, $option);

上の形でresponseが取得出来るはずです

ではJSONで送りたい場合は?

JSONでのPOST

GuzzleSampleController.php
    $client = new Client();

    $headers = [
        'Authorization' => 'Bearer ' . $token,
        'Content-Type' => 'application/json',
    ];
    $options = [
        'id1' => $id1),
    ];

    $response =  $client->request('POST', $url, [
        'json' => $options,
        'headers' => $headers,
    ]);

Guzzleを使ってみた感想

PHPからAPIを叩く事がなかったので、じゃあなんで今までajaxにばかり頼る所にしか
いなかったのか。
そこは同期か非同期かの違いだけなのかな?
ただあまり処理があっちこっち飛んだり
PHPからJSに値渡さないといけないし
やることがバラバラになるイメージがあるので、PHPだけで完結するのもありなのだなと
今回の習得した結果です

コメントください

ツッコミとかなんでも、、、もしあればコメント頂けると
今後の励みにしたいと思います。

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

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

Cannot end a section without first starting one. [Laravel]

Cannot end a section without first starting one.

bladeテンプレートの構文エラーと発生する原因について

発生する原因

シンタックスエラーの可能性:セクションが正しく囲われていないケース

master.blade.php
@section
 記述
@endsection

タイポ:共通レイアウトの呼び出し名が間違っているケース

master.blade.php
// 呼び出した共通レイアウト名が正しいか確認する
@extends('layouts.base') 

タイポ:構文が間違っているケース

master.blade.php
// sectionがsecitonになっている
@seciton('main')
  <p>{{ $msg }}</p>
@endsection
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LaravelのMailで、機能ごとに送信元アドレスを切り替えたい(Gmailなど)時にめっちゃ詰まった話

顧客「Aの機能の時は、hoge@gmail.comで確認メールを送信して欲しいんだよね」

ぼく「わかりました」

顧客「Bの機能の時は、hogehoge@gmail.comで確認メールを送信して欲しいんだよね」

ぼく「承知の助」

1時間後

ぼく「ん?むずくね?」

というわけで、詰まったお話

なんで詰まったのか

LaravelのMailってfromとかのfunctionがあって一見簡単に切り替えられるように見えるんですけど

こんな感じに変えられそう
Mail::from('hogehoge@gmail.com')
      ->to($request->user_mail)
      ->send(new Mail($request->user_name));

まぁ当たり前なんですけど、ダメなわけで

そもそもGmailを使用するには、当然usernameとpasswordを使用しているわけで、それを.envに書いて読み込んでるわけですよね

.env
MAIL_DRIVER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=465
MAIL_USERNAME= hoge@gmail.com
MAIL_PASSWORD=passpasspasspasspass
MAIL_ENCRYPTION=ssl

要はこの
MAIL_USERNAMEと
MAIL_PASSWORDはいつ読み込まれて、どうやって変更するんだ!!
って悩んだわけですよ。

参考にさせてもらったのが↓

https://qiita.com/dublog/items/3314ca25a90e76f63b17

かなりわかりやすくプロセスのライフサイクルとサービスコンテナについて書いてくれていて助かりました。

かいつまんで説明すると

・ユーザーからリクエストがくる
・app.phpがMailServiceProviderをコンテナに格納する
・内部でTransportManagerがnewされる(singleton)
・createSmtpDriver()でusernameとpasswordがconfigから読み込まれる
・メールをsendする際に、読み込まれたusernameとpasswordなどからメールが送信される

という流れなわけですよ。

これsingletonとか諸々の影響で、後から書き換えられない(できるかもしれないけど)せいでまぁえらい時間がかかりました。

結局どうやったの?

こうやりました↓

controllerの内部
extract(\Config::get('temp_mail'));

$transport = (new \Swift_SmtpTransport($host, $port))
    ->setUsername($username)
    ->setPassword($password)
    ->setEncryption($encryption);
\Mail::setSwiftMailer(new \Swift_Mailer($transport));
\Mail::to($request->user_mail)
    ->send(new TempMail($request->user_name));
config/temp_mail
<?php

return [
    'host' => 'smtp.gmail.com',
    'port' => '465',
    'username' => 'hogehoge@gmail.com',
    'password' => 'passpasspasspass',
    'encryption' => 'ssl',
];

何をやっているの?

既存のSwiftMailerだと
transport(usernameとかpasswordとか持ってるあれ)
がcontrollerに処理が届く前に、configから読み込まれて上書きできないので
もう1から$transportを作って、 SwiftMailerもろともnewしちゃいましょうって解決法でした。

これは
https://laravel.io/forum/07-22-2014-swiftmailer-with-dynamic-mail-configuration
にて参考にさせてもらいました。

というかね

本当にlaravelのMailでさくっと出来ないんですかね・・・?

めちゃくちゃ頻発する案件だと思うんですが。。

何か他にさくっとできる方法があったら共有していただきたいです。

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

効果的な簡単のWebサーバセキュリティ対策「PHP編」

今日では、PHPを使って開発されたウェブサイトやウェブアプリケーションは、世界のインターネットの80%を占めています。PHPは登場以来、多くの問題を抱えています。その中に最も重要なのは、様々なセキュリティ問題です。

確かに、PHPのカーネルには非常に多くの脆弱性がありるですが、でも今まで、セキュリティ問題のほとんどは、PHPカーネルではなく、ウェブアプリケーションのロジック自身の脆弱性に起因しています。

Webアプリケーション自身の問題を置いといて、サーバー内のPHP環境の設定が間違っていることがもう一つの原因だ。

セキュリティ対策前提条件

FastCGIモードやapache2handlerモードなど、どのような方法でphpを実行しても、rootユーザーとしてphpを実行することはいけません。そうでなければ、すべてが無意味になってしまう。

できるだけ、最新バージョンのPHPを使用する

PHPの最新版は一般的に多くの脆弱性を修正しています。

セキュリティエンジニアとしての私の視点では、例えば今最新版のPHPは7.4.7、それバージョンのPHPを使われば、利用できるの脆弱性がほとんどありません。

でも、なぜその部分のタイトルは「できるだけ」をかいていますか?

あなたもPHPアプリケーションの開発者なら、もしマイナー版のバージョン番号が20以下のバージョン使用するとき、たくさん知らないのバグはあなたを待てているそのようなことがあります。

PHP環境のセキュリティ設定

それでは、PHP環境のセキュリティ設定を始めましょう。

リスク関数を無効化

以下のPHP関数は、ほとんどのWebShellが利用している。 そのため、それらを無効にする必要があります。

passthru exec system chroot 
chgrp chown shell_exec proc_open 
proc_get_status popen ini_alter ini_restore 
dl openlog syslog readlink 
symlink popepassthru stream_socket_server 

そのため、php.iniファイルの disable_functions パラメータを変更する:

disable_functions = passthru,exec,system,chroot,chgrp,chown,shell_exec,proc_open,proc_get_status,popen,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,stream_socket_server

ただし、この設定では「eval()」関数を無効にすることはできません。なぜなら、これはPHPのカーネルエンジン「ZendEngine」が提供するの関数だからです。

PHPバージョン番号を非表示

デフォルトでは、サーバーは使用しているのPHPバージョンの番号をHTTPのレスポンスヘッダ部分で見せる。

例えば:

Snipaste_2020-07-08_23-52-55.png

これは、特定のバージョンPHPの脆弱性に対する攻撃をもたらす可能性があります。だから、安全のためには、PHPバージョン番号の表示をオフにする必要があります。

同じに、php.iniファイル内の設定に変更する:

expose_php = Off

エラーメッセージの表示を非表示

エラーメッセージには攻撃者が利用できる情報が大量に含まれており、だから、本番環境ではエラーメッセージの非表示するも必要だ。

同じに、php.iniファイル内の設定に変更する:

display_errors = Off

また、エラーを見つけやすくするために、ローカルのエラーログ機能を有効にすることもできます。

log_errors = On
error_log  = /var/log/php_error.log

リモートファイルへのアクセス禁止

この機能を使うことで、PHPアプリケーションの脆弱性を利用することがもっとやすい。

特に最近では、ほとんどの攻撃でPHPのデシリアライズが利用している。 この機能を使用すると、Pharのデシリアライズ攻撃のために、問題がある Phar ファイルパッケージを簡単にロードすることがもっとやすい。

同じに、php.iniファイル内の設定に変更する:

allow_url_fopen =  Off
allow_url_include = Off

もし、リモートダウンロード機能を使いたいとき、cURL関数を使いでください。

PHPアプリケーションのフォルダアクセス権限をリミット

PHPのデフォルト設定では、PHPスクリプトプログラムがサーバー中の任意のファイルにアクセスできる。これはとでも危険なことだ。

このリミットを指定するなら、サーバー侵入による被害を大幅に軽減することができます。

通常、php.iniファイル内の設定に変更するができる:

open_basedir = /data/www-data:/tmp/:/var/tmp/:/proc/

しかし、私はこのパラメータをWebServerのWebホスト設定ファイルに設定することをお勧めします。

Apacheではこのように設定しています:

<VirtualHost *:80>
......
php_admin_value open_basedir "/data/www-data:/tmp/:/var/tmp/:/proc/"
......
</VirtualHost>

nginxのfastCGIモードで使用するのとき、このWebホストのデフォルトディレクトリにある「.user.ini」ファイルを修正するか、作成する必要があります。

通常、このファイルを直接修正することはできません。そこで、このファイルのロックを解除するため、まず以下のコマンドが必要です:

chattr -i /data/www-data/.user.ini

そして、このファイルの内容を変更します:

open_basedir=/data/www-data:/tmp/:/var/tmp/:/proc/

変更後、再度このファイルをロックします:

chattr +i /data/www-data/.user.ini

ロックが完了した後、設定の自動有効が有効にするまでに最大5分程度かかる場合があります。 あるいは、PHP-FPMを再起動して設定をすぐに有効にすることもできます。

session.upload_progress.enabledを禁止する

この機能を使うことで、PHPアプリケーションの脆弱性を利用することがもっとやすい。大体効果とallow_url_fopenallow_url_include同じだ。

同じに、php.iniファイル内の設定に変更する:

session.upload_progress.enabled = Off

PHP-FPM(Nginx.FastCGIなど)モードで、chrootとchdirを設定する

chrootとchdir設定は、PHP-FPMモードで専門な設定です。その設定の効果簡単に説明なら、それはopen_basedirのパワー強化版。この機能は、各WEBホストの作業ディレクトリを完全に分離します。

しかし、それは使用するための巨大な障壁を作ることになる。例えば、「sendmail」の機能が使えなくなります。 また、任意のドメインにアクセスすることもできませんなど。

chroot 機能を使用するためには、他にも変更が必要な設定がたくさんあるので、ここでは詳しく説明しません。その後、chrootedの設定方法を記事にします。

また、こちら(PHP Documentation chroot)をクリックすると詳しい説明が表示されます。

PHP アプリケーションの管理用バックエンドのパスを予測不可能な名前に変更します。

この対策も実はとても重要です。

世界のインターネット中には、悪質な脆弱性スキャナーがたくさん存在します。

例えばたくさん悪い脆弱性スキャナーはphpmyadminのデフォルトパスにスキャナーしたいです。

正直言ば、phpmyadminがバグフリーであることを保証できない。

なお、一番簡単な方法は、phpmyadminを簡単にスキャンアウトさせらねないことです。

例えば、「.../1ba286020e414afb/...」このようなみたいを変更します。

同じに、WordPressのWP-adminようなのコントロールパネルは同じの方法に対応する。

結論

上記のセキュリティ設定したばいは、PHPサーバーのセキュリティを最大化したり、サーバーが侵害された後の被害を最小限に抑えたりするためのものです。

しかし、PHPアプリケーションの開発者が十分にセキュリティを意識がなければ、サーバーへの侵入の可能性を防ぐことはできません。

もしあなたがPHP開発者であれば、ひとつ覚えておいてください:

「eval()」関数、絶対に使わないでください。

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

Slim 3とSlim 4のContainerInterfaceはnamespaceが違う

背景

社内にSlim 3のPJとSlim 4のPJがあって、Slim 4側は最近実装された新規開発でだいぶきれいに書かれていたので、レガシーなプロジェクト(Slim 3)のリファクタリング時にSlim 4の実装を真似していたらハマりました。
(こんな状況はなかなかないので参考にならなそうですが。。。)

TypeError: Argument 1 passed to app\helpers\XxxHelper::__construct() must be an instance of Psr\Container\ContainerInterface, instance of Slim\Container given, called in /xxx/app/app.php on line 75 in xxxxx.php on line 19

結論

Slim\Containerを見てもimplements ContainerInterfaceしてるのになぜ?ってなってしまいましたが、
よくよくその定義を確認すると、Slim 3とSlim 4では ContainerInterface のnamespaceが違いました。
参考: https://stackoverflow.com/a/38270475

Slim 3: Interop\Container\ContainerInterface
Slim 4: Psr\Container\ContainerInterface

コピペしすぎに気をつけます。。。

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

cookieで条件分岐するときはjQueryよりPHPがいいってハナシ。

「同じページに2回目以降は表示させない」

というのをやりたかったのですが、
つまったので、苦労の後を記録しておきます。

jQueryの場合

jQueryのクッキー使うときは、以下のように

  • jQuery
  • jquery.cookie.js
  • cookieを発火させる記述

の順番に記述します。

<script src="js/jquery-3.4.1.min.js" type="text/javascript"></script>
<script src="js/jquery.cookie.js" type="text/javascript"></script>
<script>
    //クッキーがあれば、#js-loaderを非表示にする
    if ($.cookie("access")) {
      $("#js-loader").css('display','none');
    }
    //クッキーを取得させる
    $.cookie("access","topPage"); //2つ目は任意の文字を入れます
</script>

こちら参考にしました
-> jQueryプラグイン「jquery.cookie.js」でcookieを簡単に扱う

これでも機能したのですが、
javascriptの読み込みが遅い場合、
#js-loaderが最初にちょこっと表示されてしまいます!!!

これだとクッキーで条件分岐する意味がない。。。。

なので、最初っからキレイに非表示にしたい場合、
PHPを使いましょう!

PHPの場合

以下のように記述しました。
まず、最初のページに表示させる条件分岐を書き、
その中に、クッキーがあるかどうかで条件分岐させます。

<?php if(is_front_page()):
      if(!(isset($_COOKIE["topPage"]))): //クッキーが保存されていない時 []内は任意の文字
      setcookie("topPage", 1); //クッキーをいれる 2つ目は任意の値を入れます ?>
 <div id="js-loader">
  ローディングアニメーションの要素
 </div>
<?php endif; //クッキーの分 ?>
<?php endif; //is_front_pageの分 ?>

これでスッキリ解決しました!!

こちら参考にしました!
-> PHPでCookieを使う方法【初心者向け】
-> クッキーの読み込み

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