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

YYPHP#93「PHPの勉強何から始めたらいい?」 「一番やらかした失敗談は何?」 「ソースコードが酷すぎる現場で生き抜く術」 「テストコードを書くタイミングって?」

これは2019年7月19日に開催したPHPerイベントYYPHP#93のイベントレポートです。

YYPHPは一言で「PHPerの部室」です。PHPについて、雑に、ゆるく、ワイワイ話し合う集いです。毎回お題を決めずに雑談を出発点にいろいろなことを突発的にやります。集まった人でコードリーディングをすることもあれば、一緒に開発ツールを触ってみたり、フレームワークについての情報交換をすることもあります。開催はほぼ毎週、高田馬場にて。

今回の配信動画

過去回の配信動画

https://www.youtube.com/playlist?list=PLpOeTEye3Bg6PodrLHHC72jWMJYZz8VbG

前回YYPHP#92「なぜDockerは日本で受け入れられてないのか?」「RiinさんにLTのコツを聞く」「オレオレフレームワークからLaravelに移植する上での悩み」「インターフェースって何?」 - Qiita

雑談

PHPの勉強何から始めたらいい? (ふるちゃん)

SQLとPythonの経験がある場合。

  • 何を目指すのかによるかも
  • 目的を作る
  • 例えば。。
    • WordPressのカスタマイズができるようになる
  • 公開しなくてもいいからサービスを作ってみるのがいい。
    • サービスを作ってみると、よく使うものが何なのか分かる。
    • 自分で使って便利なサービスを作るのもあり。
    • ウェブが考えることが多いので、CLIアプリから作るといいかも。
      • 覚えることが少ないけど、PHPでよく使う機能は使うから勉強になる。
  • 参考書を読んで、写経する
    • サービスといっても作りたいものが無かったので。
    • 自分で考えていちから作ったほうが、技術として身につく。
  • Amazon.co.jp: 独習PHP 第3版 eBook: 山田祥寛: Kindleストア
  • パーフェクトPHP | 小川雄大, 柄沢聡太郎, 橋口誠 | 工学 | Kindleストア | Amazon

みんな、どういう勉強のしかたをしてる? (はたけ)

ひたすら業務(自社サービス)を開発しているが、勉強という概念がわからなくなっている。
ひたすら業務すること=勉強みたいになっている。
聞きたいこととしては:

  • どんな勉強している?
  • いつ勉強している?

...

  • 業務で取り扱っている技術を、業務時間外で深く調べてみる。
  • 集中したほうがいい?
    • なにかしら一つ極めたほうが、他に行きやすいと思う。
      • 結果的に効率がいいはず。
  • 朝7:30に会社近くのカフェに行って勉強している。
  • リファクタリングを読んでいるがJavaなので苦戦している。
    • Java→PHPに書き換えることで結構勉強になると思う。
  • 家に帰ってから、興味がある分野(サーバサイド)について調べたり、読書したり。
  • Kaggleを使い倒す。
  • 疑問点を放置しない。

エンジニアとして伸びる心構えとは? (はたけ)

  • reoringが意識していること
    • 楽しむ
    • 技術に対する愛情
    • 探究心
  • nouphetさん
    • 自分の適性が合っているのを見つけたほうが伸びる
    • いろんなものを触ってみて、チャレンジしてみて、得意を探すのも大事
      • 発散 → 選択 → 集中
  • Bulkerさん
    • 自分が愉しむのが一番だと思う

エンジニアになってから一番やらかした失敗談は何? (いっとく)

  • 本番サーバで誤って rm -rf /<space>hoge/hoge
    • rm -rf $DIR/$VAR$DIR$VARも定義されていなかったというミスも。
      • set -eux を習慣化しよう
  • ターミナルが右クリックしたらクリップボードの中身が貼り付けられる設定だった
    • 大量にコピーされて、どこかのファイルが破損、バッチが起動しなくなり怒られた
  • SPAMメッセージ抽出
    • 個人情報を扱うのに、外部環境につないでやっていた
  • スイッチを何気なくつないだら社内のネットワークが全部落ちた
    • パケットが循環してしまうネットワークになってしまっていた。
    • パケットストームが発生。
  • ファイルの削除を再帰処理
    • バグで下位ディレクトリだけでなく、上位ディレクトリの中身も消えた。

ソースコードがひどい現場に入ったときでも成長できる方法って何? (いっとく)

1600行のメソッドがあったり。

  • できるだけどうすればもっと保守しやすくなるか?というのを考えながらやってる。
  • キレイなコードをさっと出せるようにするには?
    • 何がキレイかは最初はわからないと思う。
      • キレイが分かっている人に見てもらうのが一番いい
    • 綺麗さが大事じゃなくて、いろいろ考えられたため機能美として美しさが生まれる
  • 自分が気持ち悪いと思う部分があるから、良くしたいと考えられるし、違和感について深掘りしていったほうがいい。
  • ボーイスカウトルール
    • 「来たときよりもキレイに」
  • 他の人が書いたソースを見て学ぶ

テストコード書くときはどのタイミングで書いてる? (かきうち)

  • テスト駆動開発というのをよく耳にするが、
  • テストのパターン

...

  • 慣れていればテストを先に書き上げることはできるが、
  • テストファーストとTDDは厳密には違う
  • TDD: Red Green Refactor

おすすめの技術書、教えて! (いっとく)

今まで読んだもの:

  • パーフェクトPHP
  • JavaScript忍者の極意
  • リファクタリング

...

PHP以外が弱い
PHPと関連のある分野(DB、サーバー、セキュリティ……)

...

  • スッキリわかるMySQL
    • わかりやすかった
  • 徳丸本 体系的に学ぶ 安全なWebアプリケーションの作り方
    • PHPer必読、バニラPHPerにはかなり必読
  • クリーンコード、クリーンアーキテクチャ、アジャイル開発の奥義
    • キレイなコード、いい設計について
  • クリーンコード、コードコンプリート上巻
    • この2冊を理解すれば、良いコードが何か分かる。
  • PHPエンジニアが絶対に読みたい本 - Qiita

IT系特有の疾患を持っていて転職活動中です。企業に属すかフリーランスとして頑張るか迷ってます。(ばるかー)

  • どっちにするかは人による
    • フリーランスは負担が大きい
      • 経理や営業、契約締結手続きなども自分でやらないとならない。
    • プレッシャー
  • どういう企業に入るかによって違う
  • 通勤があるかリモートワークできるかで違う

YYPHPは毎週やってます

PHPについてワイワイ話したい方は、YYPHPのイベント情報をチェックしてみて下さい。

以上、YYPHPのレポートでした。次回もワイワイやっていきたいと思います! では、また来週!

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

PHPで簡単なBANをする

攻撃的な発言をする人などをBANしたい時に.使えるかも…?

前提

  • php 7.0
  • apache2
  • ubuntu 18.04

BANしたいユーザーのipを書く(BAN.php)

BAN.php
<?php
$ip = $_SERVER["REMOTE_ADDR"];
$nguser = array('123.456.789.012', '111.222.333.444');
?>
  • 123.456.789.012とか111.222.333.444のところはBANしたいIP名に書き換える
  • 最初と最後の<?php と ?>を書かないと後々htmlとして読み込まれるので注意...
  • IP増やしたい時はコンマで区切って増やしてね

メインページでの処理(index.php)

index.php
<?php
include 'BAN.php';
if (in_array($ip, $nguser)) {
  header('Location: https:/mysite.mysite/banuser.php');
 }
?>
<!--ここからhtmlとか内容を書いてね-->

  • include 'BAN.php';のところはそれぞれの環境に応じてパスを変えてね。(この例だと同ディレクトリにBAN.phpが置いてある)
  • https://misite.misite のところは自分のサイトに書き換えてね

BANされた人向けのページ(banuser.php)

banuser.php
<?php
include 'BAN.php';
if (in_array($ip, $nguser)) {
 }else{
  header('Location: https://correctjp.work');
 }
?>
<!--ここから先は自由に書いてね-->
<h1>あなたはBANされました。</h1>
  • 別になくてもまあいいですがnot foundだとあれなので...
  • phpのところはBANされてない人にURLから飛んでこられても見せないためです。
  • リンクの書き換えとかはさっきと同じ

その他

個人的には誰にでもわかるように書いた...つもりです。
初心者が思いつきで考えたのでもっといい方法があると思います..
Apache2でBANすればいいんですが面倒だった時とかに使えればいいかもです。
どう動いてるかとかは今後追記するかもしれませんが他の人の見たりggって見てください..
一応これ、そのまま怪しい日本語ジェネレーター(https://correctjp.work )で使ってます..
お疲れ様でした!

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

CentOS 或いはEC2 にapache2.4 ,php5.4 を追加する

あらすじ

先輩に教えてもらったキーワードを元にサーバーを構築する備忘録。
今回はapache,phpを追加する。AWSのEC2にも同様の記述が使えるそうなので、そちらでも構いません。
EC2で動作確認しながら書いてます。

とりあえず2ついっぺんにインストール

$ sudo yum install httpd php 
# Is this ok [y/d/N]: が出て止まったら y,Enter でok

apache

apacheについてはこちら

# httpdを起動
$ sudo systemctl start httpd

# 確認(active:runningになればOK)
$ sudo systemctl status httpd

# システム起動時に毎回起動するように設定
$ sudo systemctl enable httpd

# 確認
$ sudo systemctl is-enabled httpd

# 起動
$ sudo systemctl start httpd.service

うまくいけばapacheのテストページが表示されます。
AWSでElastic IPに書かれてるIPをブラウザに打ち込んで入ってみましょう。もし失敗したらAWSのセキュリティグループのページを確認して、HTTPのポート80番が空いているか確認しましょう。

86153f00ca952916533bd061c9e783df.png
これでapacheはOK。

hello world!!

お約束ですね、動作確認のためやりましょう!
本来ならconfigファイルを確認したほうがいいかもしれませんが別の機会に回します。

初期設定としてapacheは/var/www/html内のファイルを最初に読むようになっています。そこにphpファイルを作成しついでphpの動作確認をするとしましょう。

$ sudo vim /var/www/html/index.php
index.php
<?php echo'hello world!!';
echo phpinfo() ?>

下のようになっていればOK!ようこそ,PHPの世界へ・・

b6dbc1b4d545b8d6591521809102ccba.png

はい!ということで必要最小限の設定はできたかと思います。本当はDBも入れようと思ったんですがec2の方はどうも勝手が違うようで(RDSあるから?)それ確認してからにします。
次回もどんどん機能を足していきましょう!

参考文献

PHP + Apacheのインストール - Linux環境 - CentOS

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

getでの処理

受け手側

$request->input('key')

例 URL
http://google.com?hoge=hoge1&test=test1

$request->input('hoge')

hoge1

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

CakePHPでAjaxのデバック

Ajaxでリクエストを飛ばした際は、CakePHPのコントーラの処理でdebug&exitで処理を止めて見ることができません
CakePHPなどのフレームワークを使う場合はレスポンスにエラーが返されるので、それをHTMLファイルに貼り付けて、エラー箇所を特定する方法を紹介します。

Ajaxの流れ

  1. ビューからコントローラにjsでリクエストを送る
  2. コントローラで受け取った通信に含まれている値で処理を行い、ビューにレスポンスを送る
  3. コントローラからビューに送られてきたレスポンスを受け取り一連の流れが終了

1. jsコード

function userAdd(id, name) {
    let userId = '#userIdFlg' +  '_' + id;
    let userName = '#userNameFlg' +  '_' + name;
    // Ajax による POST 送信
    $.ajax({
        url: "/users/userAdd/",
        type: "POST",
        data:{
            user_id : $(userId).val();
            name : $(userName).val();
        },
        dataType: "text",
        success : function(response){
            alert("成功");
        },
        error: function(){
            console.log(response);
        }
    });
}

2. コントローラ

Usersコントローラ

UsersController.php
public function userAdd()
{
  if ($this->request->is(['ajax'])) {
    $this->autoRender = false;
    $this->loadModel('Users');
    $data = $this->request->data;
    $user = $this->Users->newEntity();
    $user = $this->Users->patchEntity($user, $data);
    $this->Users->save($user);
    return;
  }
}

3. htmlでエラーコードを見る

コントローラの処理でエラーがあった場合に、ビューに送られるレスポンスにエラーが含まれている場合があります。
デベロッパーツールでconsole.log(response)の中身を見てみましょう。

training_progress.js?1562823821:21 <pre class="cake-error"><a href="javascript:void(0);" onclick="document.getElementById('cakeErr5d26d2a3581d1-trace').style.display = (document.getElementById('cakeErr5d26d2a3581d1-trace').style.display == 'none' ? '' : 'none');"><b>Notice</b> (8)</a>: Undefined index: content_id [<b>APP/Controller/TrainingController.php</b>, line <b>563</b>]<div id="cakeErr5d26d2a3581d1-trace" class="cake-stack-trace" style="display: none;"><a href="javascript:void(0);" onclick="document.getElementById('cakeErr5d26d2a3581d1-code').style.display = (document.getElementById('cakeErr5d26d2a3581d1-code').style.display == 'none' ? '' : 'none')">Code</a> <a href="javascript:void(0);" onclick="document.getElementById('cakeErr5d26d2a3581d1-context').style.display = (document.getElementById('cakeErr5d26d2a3581d1-context').style.display == 'none' ? '' : 'none')">Context</a><pre id="cakeErr5d26d2a3581d1-code" class="cake-code-dump" style="display: none;">
<code><span style="color: #000000"><span style="color: #0000BB">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$mg_checks&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">MgChecks</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">find</span><span style="color: #007700">()</span></span></code>
<span class="code-highlight"><code><span style="color: #000000"><span style="color: #0000BB">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">where</span><span style="color: #007700">([</span></span></code></span>
<code><span style="color: #000000"><span style="color: #0000BB">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #007700">[</span><span style="color: #DD0000">'content_id'&nbsp;</span><span style="color: #007700">=&gt;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'content_id'</span><span style="color: #007700">]],</span></span></code></pre><pre id="cakeErr5d26d2a3581d1-context" class="cake-context" style="display: none;">$progress_code = &#039;1&#039;
$level_code = null
$group_code = null
$loginuser = [
    &#039;id&#039; =&gt; (int) 1,
    &#039;login_code&#039; =&gt; &#039;opema_sys&#039;,
    &#039;nice_name&#039; =&gt; &#039;テスト 管理者(創新ラボ)&#039;,
    &#039;email&#039; =&gt; &#039;&#039;,
    &#039;sei&#039; =&gt; &#039;テスト&#039;,
    &#039;mei&#039; =&gt; &#039;管理者(創新ラボ)&#039;,
    &#039;kana_sei&#039; =&gt; &#039;テスト&#039;,
    &#039;kana_mei&#039; =&gt; &#039;カンリシャ2&#039;,
    &#039;section&#039; =&gt; &#039;本部&#039;,
    &#039;position&#039; =&gt; &#039;24&#039;,
    &#039;birth&#039; =&gt; null,
    &#039;joined&#039; =&gt; null,
    &#039;img_path&#039; =&gt; null,
    &#039;role&#039; =&gt; &#039;9&#039;,
    &#039;status&#039; =&gt; null,
    &#039;onetime_password&#039; =&gt; null,
    &#039;onetime_password_limit&#039; =&gt; null,
    &#039;passport&#039; =&gt; &#039;&#039;,
    &#039;keycode&#039; =&gt; null,
    &#039;created&#039; =&gt; object(Cake\I18n\FrozenTime) {

        &#039;time&#039; =&gt; &#039;2018-11-15T02:52:25+09:00&#039;,
        &#039;timezone&#039; =&gt; &#039;Asia/Tokyo&#039;,
        &#039;fixedNowTime&#039; =&gt; false

    },
    &#039;modified&#039; =&gt; object(Cake\I18n\FrozenTime) {

        &#039;time&#039; =&gt; &#039;2018-11-19T18:43:34+09:00&#039;,
        &#039;timezone&#039; =&gt; &#039;Asia/Tokyo&#039;,
        &#039;fixedNowTime&#039; =&gt; false

    },
    &#039;shop_id&#039; =&gt; (int) 32,
    &#039;memo&#039; =&gt; &#039;&#039;,
    &#039;level_id&#039; =&gt; (int) 4,
    &#039;affiliation_dept&#039; =&gt; null,
    &#039;store_in_charge&#039; =&gt; &#039;&#039;
]
$data = []
$loginID = (int) 1
$ajwh_group_no = [
    &#039;group_no is&#039; =&gt; null
]</pre><pre class="stack-trace">App\Controller\TrainingController::progress() - APP/Controller/TrainingController.php, line 563
Cake\Controller\Controller::invokeAction() - CORE/src/Controller/Controller.php, line 440
Cake\Http\ActionDispatcher::_invoke() - CORE/src/Http/ActionDispatcher.php, line 119
Cake\Http\ActionDispatcher::dispatch() - CORE/src/Http/ActionDispatcher.php, line 93
Cake\Routing\Dispatcher::dispatch() - CORE/src/Routing/Dispatcher.php, line 60
Cake\Controller\Controller::requestAction() - CORE/src/Routing/RequestActionTrait.php, line 177
Cake\Controller\Component\RequestHandlerComponent::beforeRedirect() - CORE/src/Controller/Component/RequestHandlerComponent.php, line 285
Cake\Event\EventManager::_callListener() - CORE/src/Event/EventManager.php, line 414
Cake\Event\EventManager::dispatch() - CORE/src/Event/EventManager.php, line 391
Cake\Controller\Controller::dispatchEvent() - CORE/src/Event/EventDispatcherTrait.php, line 78
Cake\Controller\Controller::redirect() - CORE/src/Controller/Controller.php, line 551
App\Controller\Trainin

上記のようなレスポンスが返ってきています。この中にエラーメッセージがあります。上部の方にHTML形式でエラーメッセージが返されているので、htmlファイルを作って、その中にコピペして見てましょう!

index.html
<pre class="cake-error">
  <a href="javascript:void(0);" onclick="document.getElementById('cakeErr5d26d2a3581d1-trace').style.display = (document.getElementById('cakeErr5d26d2a3581d1-trace').style.display == 'none' ? '' : 'none');">
    <b>Notice</b> (8)
  </a>
  : Undefined index: content_id [
    <b>APP/Controller/TrainingController.php</b>,
    line <b>563</b>
  ]
  <div id="cakeErr5d26d2a3581d1-trace" class="cake-stack-trace" style="display: none;">
    <a href="javascript:void(0);" onclick="document.getElementById('cakeErr5d26d2a3581d1-code').style.display = (document.getElementById('cakeErr5d26d2a3581d1-code').style.display == 'none' ? '' : 'none')">Code</a> <a href="javascript:void(0);" onclick="document.getElementById('cakeErr5d26d2a3581d1-context').style.display = (document.getElementById('cakeErr5d26d2a3581d1-context').style.display == 'none' ? '' : 'none')">Context</a>
    <pre id="cakeErr5d26d2a3581d1-code" class="cake-code-dump" style="display: none;"><code><span style="color: #000000">
      <span style="color: #0000BB">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$mg_checks&nbsp;</span><span style="color: #007700">=&nbsp;</span>
      <span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span>
      <span style="color: #0000BB">MgChecks</span>
      <span style="color: #007700">-&gt;</span>
      <span style="color: #0000BB">find</span>
      <span style="color: #007700">()</span></span>
</code>
      <span class="code-highlight">
      <code><span style="color: #000000"><span style="color: #0000BB">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">where</span><span style="color: #007700">([</span></span></code></span>
<code><span style="color: #000000"><span style="color: #0000BB">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #007700">[</span><span style="color: #DD0000">'content_id'&nbsp;</span><span style="color: #007700">=&gt;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'content_id'</span><span style="color: #007700">]],</span></span></code></pre><pre id="cakeErr5d26d2a3581d1-context" class="cake-context" style="display: none;">$progress_code = &#039;1&#039;

するとブラウザ側でコントローラのエラーが確認できますね。

スクリーンショット 2019-07-11 16.37.30.png

※上記のスクショと今回紹介したコードは関係ありません。イメージを掴んでもらうためにスクショを載せました。

Ajaxのデバック 番外編1(Chromeデベロッパーツール)

  1. デベロッパーツールを開く
  2. Networkタブをクリック
  3. POST通信を走らせる
  4. Nameにある該当ファイルをクリック

これでStatus Codeや送られたデータであるForm Dataが確認できますね。

ログをファイルに書き込んで処理を確認

例えば下記のようなAjaxの処理がコントローラに記述されているとします。

UsersController.php
    public function addUser()
    {
        $this->autoRender = false;
        $this->loadModels('Users');
        $result = false;
        if ($this->request->is(['ajax'])) {
            $data = $this->request->data;         
            $user = $this->Users->newEntity();
            $user = $this->Users->patchEntity($user, $data);
            if ($this->Users->save($user)) {
                $result = true;
            }
        }
        return $result;
    }

これをロギング設定をします。use Cake\Log\Log;に記述して、確認したい変数を変数の部分に記述します。$this->log('変数', 'debug');

UsersController.php
    use Cake\Log\Log;

    public function addUser()
    {
        $this->autoRender = false;
        $this->loadModels('Users');
        $result = false;
        if ($this->request->is(['ajax'])) {
            $data = $this->request->data;         
            $user = $this->Users->newEntity();
            $user = $this->Users->patchEntity($user, $data);
            $this->log($user, 'debug');
            if ($this->Users->save($user)) {
                $result = true;
            }
        }
        return $result;
    }

/logs/debug.logに書き込まれます。

Ajaxのデバック 番外編3(コントローラに擬似的に処理を飛ばす)

Viewから飛んできたが値がどう処理されるか確認した場合がありますよね。そんな時の確認方法です。

コントローラの処理↓

UsersController.php
    public function addUser()
    {
        $this->autoRender = false;
        $this->loadModels('Users');
        $result = false;
        if ($this->request->is(['ajax'])) {
            $data = $this->request->data;         
            $user = $this->Users->newEntity();
            $user = $this->Users->patchEntity($user, $data);
            if ($this->Users->save($user)) {
                $result = true;
            }
        }
        return $result;
    }

上記の処理はAjaxでビューから通信が走った時に処理されます。この部分があるからですね→ if ($this->request->is(['ajax'])) {

このif文を一時的にコメントアウトし、下記のアクションに遷移して通信を擬似的に発生させてdebugして確認します。

UsersController.php
    public function addUser()
    {
        //$this->autoRender = false;
        $this->loadModels('Users');
        $result = false;
        $data['id'] = 11;
        $data['name'] = '西村';
        //if ($this->request->is(['ajax'])) {
            $data = $this->request->data;         
            $user = $this->Users->newEntity();
            $user = $this->Users->patchEntity($user, $data);
            debug($user);exit; // $userにどんな結果が返ってきているか確認
            if ($this->Users->save($user)) {
                $result = true;
            }
        //}
        return $result;
    }

 参考

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

NixOS で PHP 環境作ったった

なにこれ?

NixOS でPHPアプリケーションサーバを作ってみました。

NixOS はパッケージマネージャ Nix を利用した Linux ディストリビューションで、宣言的な記述でシステム環境を構築することが可能で、インストールパッケージの細かい制御が可能な点が魅力です。
イメージとしては Ansible が組み込まれた Linux といったところでしょうか。
またパッケージマネージャ Nix は既存の Linux 環境に導入が可能です。

NixOS には独自のコンテナ機能があったり、クラウドへのデプロイまで済ませられる NixOps コマンドを提供していたり、色々と魅力的に思えます。

本稿では 基本的な設定方法とハマりやすい点について説明し、最後に設定ファイルを紹介します。

インスタンスを立ち上げる

まずは AWS で EC2 インスタンスを立ち上げます。公式が AWS 向けの AMI を公開していますので、それを使用できます。
インスタンスタイプは、今回の構成では t2.micro でも十分です。ただしディスクスペースをかなり使うので、最低でも 10 GiB は取っておいたほうが良さそうです。

インスタンスが立ち上がったらログインしましょう。初期ユーザは root です。

NixOS の環境設定の基本

続いて環境設定ファイルを記述しましょう。環境設定ファイルは /etc/nixos/configuration.nix です。後述する nixos-rebuild コマンドを実行すると、このファイルに基づいた環境が構築されます。デフォルトでは以下のような記述になっているかと思います。

{
  imports = [ <nixpkgs/nixos/modules/virtualisation/amazon-image.nix> ];
  ec2.hvm = true;
}

configuration.nix は nix 言語で記述されています。 nix 言語をインタラクティブに実行することの出来る nix-shell という処理系もありますが、今回は使用しません。

変数 imports にはファイルパスのリストが束縛されます。 nix 言語では複数のファイルに分割して設定を行っていくことが可能で、 imports に設定されたリスト中のパスのファイルを読み込んでいき、それを評価してオプションに値を束縛をしてゆきます。これはモジュールなどと呼ばれます。

ec2.hvm は EC2 インスタンスを使用するときのオプションの一つで、 HVM 仮想化を利用する際に設定する必要がある値です。 NixOS はこういったオプションに値を束縛していくことで設定を行います。 NixOS はオプションの検索ページを用意しており、大半のオプションが検索可能です。

以下のコマンドを実行することで環境のテストビルドをできます。

nixos-rebuild -v test

-v オプションは冗長出力オプションです。ビルド中、様々なファイルが評価されていることが見て取れるはずです。
引数の test は nixos-rebuild のサブコマンドで、テストビルドを行う事を指定しています。テストビルドの場合は再起動するともとの環境に戻ります。
ブートローダにもインストールして、起動時から立ち上げたい場合は nixos-rebuild switch を実行しましょう。

特定のパッケージを使用する

まずは使用するパッケージコレクションのリビジョンを固定しましょう。
NixOS のパッケージコレクションは、nixpkgs という名前で github で公開されています。
この nixpkgs レポジトリのリビジョンを固定することで、インストールされるパッケージのバージョンを固定することができます。
また、リビジョンの他にブランチを指定することも可能です。この場合はリリース毎のブランチを指定することである程度パッケージのバージョンを固定しつつ、セキュリティパッチだけあてるなどが出来そうです。

今回は利用するブランチを、 "release-19.03" に固定しましょう。
configuration.nix に以下のパッチを当てます。

0a1,7
> let
>   pkgsSrc = (builtins.fetchGit {
>     url = https://github.com/nixos/nixpkgs;
>     ref = "release-19.03";
>   });
>   pkgs = import pkgsSrc {};
> in

パッチの内容を cat などを使ってファイル、 patch などに書き出し、 patch configuration.nix patch などとすることでパッチを当てることが出来ます。

builtins.fetchGit は git レポジトリを取得する nix 組み込み関数で、 { url = ... } は fetchGit に渡す引数です。 pkgsSrc にダウンロードしたレポジトリが束縛され、これを import して引数なしで評価して pkgs に束縛しています。

fetchGit は git コマンドを利用しますが、 NixOS にはデフォルトでは git コマンドがインストールされていないので、このまま nixos-rebuild を実行しても途中で失敗してしまいます。以下のコマンドを実行して git をインストールしましょう。

nix-env -iA nixos.git

基本的なパッケージのインストール

パッケージコレクションを指定したので、今度はこのコレクションからパッケージをインストールしてみましょう。
NixOS はデフォルトでは git はもちろん、 syslog デーモンや vim エディタすら入っていません。試しに file コマンドを実行して、インストールされていないことをみましょう。

続いて今回使用するパッケージをインストールしましょう。
configuration.nix に以下のパッチを当てます。

10a11,14
>
>   environment.systemPackages = with pkgs; [
>     rsyslog git vim file nginx php73
>   ];

オプション environment.systemPackages に、インストールしたいパッケージのリストを束縛します。右辺の冒頭で with pkgs; とすることで、リスト内のパッケージは全て先ほど指定したパッケージコレクションの名前空間から選択されます。

nixos-rebuild を実行すると、パッケージがインストールされた環境が構築されます。試しにまた file コマンドを実行してみましょう。
利用可能なパッケージは nix search コマンドで検索をすることができます。

環境設定時の Tips

オプション束縛値の上書き

ここまで見たように NixOS ではオプションに値を設定して環境を定義していきます。NixOS はオプションの検索ページを用意しており、大半のオプションが検索可能です。

モジュールは import することでいくつもの値を設定してくれるので便利なのですが、そういったオプションの値を変更したい場合は以下のような書き方をする必要があります。

  services.openssh = pkgs.lib.mkForce {
    permitRootLogin = "no";
    passwordAuthentication = false;
  };

値を一度 mkForce で処理してから services.openssh に束縛しています。

NixOS の束縛は全て優先度が設定されています。デフォルトの優先度は 1000 です。
複数の束縛がある場合、優先度が最も高いものが採用されます。
mkForce は優先度を 50 に設定する関数です。具体的に優先度を設定したい場合は、 mkOverride が使えます。

services.openssh.permitRootLogin には、モジュールamazon-image.nixで値が束縛されているので、 mkForce を利用する必要があります。

pkgs.lib を書く手間を減らしたい場合は、 with を用いた書き方が可能です。詳しくは configuration.nix ファイルを見てください。

モジュールの引数

今回はファイル自体で設定を完結させるため、自分でパッケージコレクションを指定しました。しかし NixOS がデフォルトで提供するものを使用することも可能です。
configuration.nix はそれ自体モジュールであり、 nixos-rebuid は pkg, lib, config などの引数を与えて評価します。
configuration.nix の冒頭を、 let ... in で始めるのではなく、以下のように記述することで簡潔に記述をすることが可能になります。

{ pkgs, lib, ... }:

configuration.nix ファイル

最終的な configuration.nix ファイルの内容は以下のようになります。
主要な設定項目は以下の通りです。

  • パッケージコレクションの指定とパッケージのインストール
  • rsyslog の設定
  • ユーザの追加、および sudo, sshd など権限まわりの設定
  • nginx, phpfpm, ファイアウォールの設定

この記述を利用する場合、ユーザ名と SSH 公開鍵については、自分自身で書き換えて使用してください。!TODO! とコメントにある行がそれです。

let 
  pkgsSrc = (builtins.fetchGit {
    url = https://github.com/nixos/nixpkgs;
    ref = "release-19.03";
  });
  pkgs = import pkgsSrc {};
  lib = pkgs.lib;
in
with lib;
{
  imports = [ <nixpkgs/nixos/modules/virtualisation/amazon-image.nix> ];
  ec2.hvm = true;

  nix.gc = {
    automatic = true;
    dates = "19:30";
  };

  environment.systemPackages = with pkgs; [
    rsyslog
    git vim file screen shellcheck
    nginx php73
  ];

  services.rsyslogd = {
    enable = true;
    extraConfig = ''
      *.*;      -/var/log/all
    '';
  };

  ################################################
  services.openssh = mkForce {
    permitRootLogin = "no";
    passwordAuthentication = false;
  };

  security.sudo.wheelNeedsPassword = false; 

  users.users.username = {           # !TODO! username にユーザ名をセット
    isNormalUser = true;
    extraGroups = [ "wheel" ];
    openssh.authorizedKeys.keys = [
      "ssh-rsa ..." ];               # !TODO! 使用する SSH 公開鍵をセット
  };

  ################################################
  networking.firewall.allowedTCPPorts = [ 80 443 ];

  services.phpfpm = {
    poolConfigs."default" = ''
        listen = /var/run/default-phpfpm.sock
        user = nginx
        group = nginx
        pm = dynamic
        pm.max_children = 32
        pm.max_requests = 500
        pm.start_servers = 2
        pm.min_spare_servers = 2
        pm.max_spare_servers = 5
        listen.owner = nginx
        listen.group = nginx
        php_admin_value[error_log] = 'stderr'
        php_admin_flag[log_errors] = on
        env[PATH] = ${lib.makeBinPath [ pkgs.php ]}
        catch_workers_output = yes
        ;; security.limit_extensions =  ;; to accept *.html file
    '';
  };

  services.nginx = {
    enable = true;
    virtualHosts."_".locations."/" = {
      root = "/var/www/default";
      extraConfig = ''
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_index index.php;
        fastcgi_pass unix:/var/run/default-phpfpm.sock;
        include ${pkgs.nginx}/conf/fastcgi_params;
        include ${pkgs.nginx}/conf/fastcgi.conf;
      '';
    };
  };
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel5】認識機能を実装した際に追加される「Auth::routes()」にオプションを指定してカスタマイズする

php artisan make:authは便利だけど登録機能は別で作りたい

という場合があると思うので、標準で実装される登録機能のルーティングを外してみたりしてみます。
※Laravel5.7系以上

ソース

php artisan make:authを実行後、ルーティングに以下のコードが自動追加されると思うので、こいつに引数を渡していく

routes\web.php
Auth::routes();

ちなみに上記のルーティング情報は以下の通りです。
WS000045.JPG

Auth::routes()に対して以下のようにオプションで引数をbooleanで渡してやるとルーティングの設定をON/OFFできる

routes\web.php
Auth::routes([
    'verify'   => true, // メール確認機能(※5.7系以上のみ)
    'register' => false, // デフォルトの登録機能OFF
    'reset'    => true,  // メールリマインダー機能ON
]);

上記のルーティング情報は以下の通り。registerが消えてverifyが追加されていると思います。
WS000046.JPG

大本のソースは以下(laravel\framework\src\Illuminate\Routing\Router.php)にあります。
コードを見てもらったらわかると思いますが、デフォルトでregister(登録機能)とreset(パスワードリマインダー)はスカフォールディングした段階で有効になるように書かれています。
(以下のソースはLaravel5.8系なので、新機能のメール確認機能verifyが記述されていてかつデフォルトで無効になるようになっています)

laravel\framework\src\Illuminate\Routing\Router.php
    /**
     * Register the typical authentication routes for an application.
     *
     * @param  array  $options
     * @return void
     */
    public function auth(array $options = [])
    {
        // Authentication Routes...
        $this->get('login', 'Auth\LoginController@showLoginForm')->name('login');
        $this->post('login', 'Auth\LoginController@login');
        $this->post('logout', 'Auth\LoginController@logout')->name('logout');

        // Registration Routes...
        if ($options['register'] ?? true) {
            $this->get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');
            $this->post('register', 'Auth\RegisterController@register');
        }

        // Password Reset Routes...
        if ($options['reset'] ?? true) {
            $this->resetPassword();
        }

        // Email Verification Routes...
        if ($options['verify'] ?? false) {
            $this->emailVerification();
        }
    }

おわり

  • 使用してない機能はルーティングをOFFにして不要なコントローラーは削除しておきましょう
  • ただし、↑の引数で設定を切り替えられるのは5.7系以前は実装されてないみたいなので注意してください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel5】たまに出てくる「the page has expired due to inactivity. please refresh and try again」を表示させない

多くの原因はCSRFトークンを記述していない場合で発生する

ググってみると多くの場合はフォーム内にCSRFトークンの記述漏れで発生していることが多いようです。
なのでまずはフォーム内にCSRFトークンの記述があるかを確認してください
Laravel5.6以前なら{{ csrf_field() }}を、
Laravel5.6系以降なら@csrfというシンプルに記述できるので、これをフォーム内に埋め込んでください。

以下Laravel5.6以前の記述例

form.blade.php
<form method="POST" action="/profile">
    {{ csrf_field() }}
    ...
</form>

以下Laravel5.6系以降の記述例

form.blade.php
<form method="POST" action="/profile">
    @csrf
    ...
</form>

以下参考になるのかもしれませんが少し情報が古いので注意
【Laravel】TokenMismatchExceptionが発生する原因 - Qiita

CSRFトークンの有効期限切れで表示される

CSRFトークンの有効期限切れで表示される場合(例えばログイン画面で長時間放置してからログインしたりする
以下のような画面が表示され、リロードしろと促されます。

419.png

正直、この画面がでても何のことかわからないし、あまりにも突然表示されたりするので、
CSRFトークンが有効期限切れの状態でログインしたりした際、ログイン画面へリダイレクトさせる処理app/Exceptions/Handler.phpに記述していきます。

laravel\app\Exceptions\Handler.php
<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Session\TokenMismatchException; // add
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;

class Handler extends ExceptionHandler
{

// ...

    /**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Exception  $exception
     * @return \Illuminate\Http\Response
     */
    public function render($request, Exception $exception)
    {
        // 「the page has expired due to inactivity. please refresh and try again」を表示させない
        if ($exception instanceof TokenMismatchException) {
            return redirect('/login')->with('message', 'セッションの有効期限が切れました。再度ログインしてください。');
        }

        return parent::render($request, $exception);
    }
}

これでTokenMismatchExceptionが発生した場合はログイン画面へエラーメッセージとともにリダイレクトさせることができる

ステータスコード(419)の場合にリダイレクトさせる

ちなみにステータスコードで判定するようにもできるので以下のようにも書けます。

laravel\app\Exceptions\Handler.php
<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Support\ViewErrorBag; // add
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; // add

class Handler extends ExceptionHandler
{

// ...

    /**
     * Render the given HttpException.
     *
     * @param  \Symfony\Component\HttpKernel\Exception\HttpExceptionInterface  $e
     * @return \Symfony\Component\HttpFoundation\Response
     */
    protected function renderHttpException(HttpExceptionInterface $e)
    {
        $this->registerErrorViewPaths();

        // 「the page has expired due to inactivity. please refresh and try again」を表示させない
        if ($e->getStatusCode() === 419) {
            return return redirect('/login');
        }

        if (view()->exists($view = "errors::{$e->getStatusCode()}")) {
            return response()->view($view, [
                'errors' => new ViewErrorBag,
                'exception' => $e,
            ], $e->getStatusCode(), $e->getHeaders());
        }

        return $this->convertExceptionToResponse($e);
    }

}

ただステータスコード419が必ずしもCSRFトークンの有効期限切れで返しているかわからないので、TokenMismatchExceptionが発生した場合のみログイン画面へリダイレクトさせた方がいいかなと思っていますがどうでしょう?

おわり

  • もっといい方法あれば教えていただきたいです草々不一

参考URL

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

Laravel で updateOrCreate を使う際に Unknown column 'id' in 'where clause' でハマった

概要

Laravel の Eloquent が持っている updateOrCreate という関数を使った。
https://readouble.com/laravel/5.7/ja/eloquent.html

MySQL でも気軽に Upsert 処理ができて便利だと思った。

実装例
App\VeryGoodSystem::updateOrCreate(
['user_id' => 12345],
['is_registered' => true]
);

いざ利用してみたところ、下記のようなエラーが出た。
Unknown column 'id' in 'where clause' ...

エラーを追うと、 updateOrCreate で実行されている SQL 文に where `id` is null という条件が付いていることがわかった。

コードを見ても、 id という変数を使っている所は無く、途方に暮れながら調べていたら、良い Q&A を見つけた。(最下部、参考サイト)

つまり、 model に primaryKey が設定されていないと default で id という文字列を使って updateOrCreate 用の SQL を作成してしまうようだった。

利用している Model に $primaryKey = 'user_id' の一行を追加することで解決した。

参考サイト

https://laracasts.com/discuss/channels/eloquent/understanding-of-updateorcreate

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

array_merge_recursiveで文字列が配列になる

array_merge_recursiveではまったのでメモ。

デフォルト値に対してPOSTされてきた値で上書きするって感じで下記の様なコードを書いてみました。

$default = [
    'Model' => [
        'title' => 'DefaultTitle'
    ]
];
$post = [
    'Model' => [
        'title' => 'PostTitle',
        'user_name' => 'Ryuji'
    ]
];
var_dump(array_merge_recursive($default, $post));

期待してる結果は下記のような感じだったんですが…

array(1) {
  ["Model"]=>
  array(2) {
    ["title"]=> 
    string(9) "PostTitle"
    ["user_name"]=> 
    string(5) "Ryuji"
  }
}

結果はこちら

array(1) {
  ["Model"]=>
  array(2) {
    ["title"]=>
    array(2) {
      [0]=>
      string(12) "DefaultTitle"
      [1]=>
      string(9) "PostTitle"
    }
    ["user_name"]=>
    string(5) "Ryuji"
  }
}

title が配列になって "DefaultTitle" と "PostTitle" が入ってきました^^;

この例だと array_merge で十分だけど、2階層目のキーの有無が増えると、array_mergeだと足りないのよね…

このときはCakePHP2だったんで、Hash::mergeつかってやりたいことは実現できたけど、なかったら自前で処理するのをつくらないとダメですかねぇ。

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

7の倍数

問題文

1以上10N以下の整数のうち7の倍数の割合を求めよ。

ソース

<?php
//入力値
$nyu = "4";
//乗算対象数値
$ten = 10;
//乗算
$jyo = pow($ten,$nyu);
//7を割る 小数点は切り上げ
$total = $jyo / 7;
$total = floor($total);
//7の倍数の割合計算
$kekka = $total / $jyo;

echo $kekka;

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

0からAWSでLAMPとLaravel環境構築(Windows)

手順

1.AWSアカウントを作成。
参照:AWS アカウント作成の流れ

2.EC2というAmazonのクラウド上の仮想サーバーを構築。
参照:AWS EC2でWebサーバーを構築してみる

3.LAMP環境を構築。
参照:チュートリアル: Amazon Linux 2 に LAMP ウェブサーバーをインストールする

4.Laravelをインストール。
参照:AWSのEC2を立ち上げてLaravelのログイン機能を動かすまで
   AWSでLaravelを立ち上げた

1.AWSアカウントを作成

AWS は Amazon Web Services の略で、Amazon のクラウドサーバーを使用できるサービスです。
一年間無料でいろんな機能を試せます。
アカウントの登録は参照(AWS アカウント作成の流れ)に沿って行えばすぐできます。

2.EC2構築

AWSにログインすると最初はこの画面が表示されると思います。
image.png
右上の地域はリージョンといい、自分の所在地に合わせて選択します。これは利用するデータセンターの場所を意味します。理論上近いほどレスポンスタイムが短いので、近いところを選びましょう。自分の場合は「東京」です。

そしたら、左上のサービスからEC2を選択。
image.png
左のメニューの「インスタンス」 → 「インスタンス作成」を選択
image.png
一番目のAmazon Linux2 AMIを選択。
image.png
無料利用枠のt2.microにチェックを入れ、
image.png
他の設定は特にいじらずセキュリティグループの設定まで飛ばします。「新しいセキュリティグループを作成する」を選択し、「セキュリティグループ名と「説明」を好きなように入力し、画像のように五つのタイプを追加します。「ソース」のところでIP指定ができます。「任意の場所」を選択するとどこでもアクセスすることができ、「マイIP」を選択すると自分が現在使用しているネットワーク環境のIPでしかアクセスできなくなります。趣味程度の個人利用でしたら任意の場所で問題ありません。
image.png
右下の「確認と作成」 → 「起動」を押してから、このように表示されます。「新しいキーペアの作成」でキーペア名を入力。重要ですが、キーペアのダウンロードは必ずしてください。サーバーにアクセスするために必要となります。
image.png
「インスタンスの作成」 → 「インスタンスの表示」を順番に進めると、インスタントの画面に戻ります。
インスタンスの状態が running になっていれば作成成功です。
image.png

プラスα(オプション)

今作ったインスタンスに固定IPを割り当てることができます。左のメニューから「Elastic IP
」 → 「新しいアドレスの割り当て」 → 「割り当て」 → 「閉じる」で固定IPを一つゲットします。リストからIPを右クリック → 「アドレスの関連付け」で作成したインスタンスを選択し結びつけれます。

3.LAMP環境の構築

①インスタンスに接続

サーバーに接続するため、ssh接続できるターミナルTeraTermをダウンロードします。インスタンス画面下の説明から固定IPとっていれば「IPv4 パブリック IP」、とっていなければ「パブリック DNS (IPv4)」をTeraTermのホストに入力して「OK」します。
image.png
image.png
次にユーザー名に「ec2-user」、秘密鍵に先ほどインスタンスを作るときにダウンロードしたキーペアファイルをセットして「OK」。
image.png
サーバーに接続できました。
image.png
直接とは関係ないですがコマンドでsudoを打つかどうかについて「root権限で`sudo`を付けた場合と付けない場合の違いに`su`は何の略?」を読めばわかると思います。

②EC2のタイムゾーン設定

Amazon EC2のタイムゾーンを日本時間に変更する方法」でわかりやすく書かれていますが、いくつか補足があります。
/etc/sysconfig/clockにはこのように変更すると書いてありますが、

# ZONE="UTC"
ZONE="Japan"
UTC=true

trueのところをfalseにしないと再起動するときにタイムゾーンがUTCに戻ることがあります。

# ZONE="UTC"
ZONE="Japan"
UTC=flase

ここでルート権限を持たないため、vimで保存するときにきっとこのようなエラーが出ると思います。

E45: 'readonly' option is set (add ! to override)

この解決法として自分がrootになるか、「[vim]read only のファイルをsudoで強制的に保存する」を参照してください。

オプションとして、「AWSの初期設定でrootパスワードを設定する」。一行目のsshなんちゃらはすでにTeraTermログイン時にできてますので無視。

lnのオプションの意味は【 ln 】コマンド――ファイルのハードリンクとシンボリックリンクを作るに書いてあります。

③LAMP環境構築

AWSの公式チュートリアル「チュートリアル: Amazon Linux 2 に LAMP ウェブサーバーをインストールする」の順序を追えばできます。自分はSQLに慣れているため、オプションの phpMyAdmin のインストールはしていません。phpMyAdmin はデータベースをGUIで管理できるツールです。

4.Laravelのインストール

①Composerをダウンロード

Composerについては「PHP開発でComposerを使わないなんてありえない!基礎編」。
以下ダウンロードコマンドです。

$ curl -sS https://getcomposer.org/installer | sudo php
$ sudo mv composer.phar /usr/local/bin/composer

これでcomposer.pharというファイルが/usr/local/bin/composer/の下に置かれます。
このcomposer.pharファイルをcomposerというコマンドで実行できたら便利なので、composerというコマンドを作ります。

alias composer='php /usr/local/bin/composer/composer.phar'

ただし、これだけだと再起動するとリセットされてcomposerが効かなくなりますので、常に成立するようにルートに存在する.bashrcというファイルに書き込みます。

$ cd ~          # ルートに移動
$ ls -la        # .bashrcがあるかどうかを確認
$ vi .bashrc    # 「alias composer='php /usr/local/bin/composer/composer.phar'」を書き込む

そしたらターミナルが起動すると読み込まれる.bash_profile.bashrcを参照するようにします。

$ vi .bash_profile    # 「source ~/.bashrc」を一番下に書き込む

②拡張ライブラリをダウンロード

Laravelをダウンロードするのに必要なライブラリーは3つあります。mbstringmysqlndxmlです。

$ sudo yum install -y php-mbstring php-mysqlnd php-xml

③Laravelをインストール

Laravel をインストールするディレクトリに移動します。自分の場合は/var/www/の下にしました。

$ cd /var/www
$ composer create-project --prefer-dist laravel/laravel

自分の場合ここで「proc_open(): fork failed errors」というエラーが出ました。「[PHP]Composer使用時に「proc_open(): fork failed errors」エラーが出た時の対処法」を見て解決できたので共有します。

-prefer-distって何ぞやと知りたい方には「composer の–prefer-distってよく使うけど何してる?」へ。

④Apacheのドキュメントルートの設定と.htaccessの有効化

$ sudo vi /etc/httpd/conf.d/custom.conf

custom.conf
# ドキュメントルート
DocumentRoot "/var/www/laravel/public"

# .htaccess 有効化
<Directory /var/www/laravel/public>
    AllowOverride All
</Directory>

を加えます。
そしたらApacheを再起動してください。

$ sudo service httpd restart

⑤パーミッションを変更

自分はパーミッションについてまだちんぷんかんぷんで、どの権限をどうすればいいか自分ではわかっていないので、他の方のブログを見たほうがいいかもしれません。基本的にパーミッションが合ってないとシステムにこのファイルに書き込めないよと怒られます。どうやらLaravelの場合はstoragebootstrapの権限を変更する必要があるらしい。

$ cd ~
$ sudo groupadd www
$ sudo usermod -a -G www ec2-user
$ exit

一度ログアウトして再度入り直して、

$ cd /var/www
$ sudo chmod -R 777 laravel/storage
$ sudo chmod -R 775 laravel/bootstrap/cache
$ sudo chown -R root:www /var/www

これでLaravelが使えるようになったはず。

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

Laravel-Excel(Maatwebsite/Excel)CSVの解釈の検証

Laravel-Excel のCSVの解釈の検証

Laravel-Excel(maatwebsite/excel) のcsvインポートの際のcsvの記述の仕方による値の解釈がどうなるのか検証を行いました。検証は下記のDummyImportクラスインスタンスをExcelファサードのtoArray()の引数として渡し、取得した行の配列の値の解釈を確認します。

app/Import/DummyImport.php
<?php
namespace App\Import;
use Maatwebsite\Excel\Concerns\ToModel;
use Excel;
class DummyImport implements ToModel
{
    public function model(array $row)
    {
        return $row;
    }
}

//DummyImportインスタンスをExcelファサードの`toArray()`の第一引数として渡す。
//併せて、対象csvファイルへのパスを第二引数として渡す。
$data = Excel::toArray(new DummyImport(), $filepath);

検証1:文字リテラルの解釈

case1.csv
"a","b","c"
a,b,c

クオーテーション""を付けない時、付ける時の違いがあるのか検証しました。

tests/Unit/CSVExtractTest.php
<?php
namespace Tests\Unit;
use Excel;
use App\Import\DummyImport;
use Tests\TestCase;

class CSVExtractTest extends TestCase
{
    /** @test */
    public function CSV検証_文字列クオーテーションありなし()
    {
        $data = Excel::toArray(new DummyImport(), base_path().'/case1.csv');
        $this->assertEquals(2, \count($data[0]));

        //クオーテーションあり
        $this->assertEquals(3, \count($data[0][0]));
        $this->assertEquals('a', $data[0][0][0]);
        $this->assertEquals('b', $data[0][0][1]);
        $this->assertEquals('c', $data[0][0][2]);

        //クオーテーションなし
        $this->assertEquals(3, \count($data[0][1]));
        $this->assertEquals('a', $data[0][1][0]);
        $this->assertEquals('b', $data[0][1][1]);
        $this->assertEquals('c', $data[0][1][2]);
    }
}
テスト結果.txt
Time: 5.22 seconds, Memory: 16.00 MB

OK (1 test, 9 assertions)

違いはないようでした。

検証2:カンマ入り文字列の解釈

文字列にカンマの入った a,a という文字列をインポートする場合はどうなるのか検証してみました

case2.csv
"a,a","b","c"
a,a,b,c
"a,a",b,c

1列目はクオーテーション""をすべての値に付けました。2列目はクオーテーション""を付けません。(おそらくこれを3カラムと解釈されない。)3列目はa,aにだけクオーテーションを付けました。

tests/Unit/CSVExtractTest.php
<?php
namespace Tests\Unit;
use Excel;
use App\Import\DummyImport;
use Tests\TestCase;

class CSVExtractTest extends TestCase
{
    protected $extractedData;
    public function setUp():void
    {
        parent::setUp();        
        $this->extractedData = Excel::toArray(new DummyImport(), base_path().'/case2.csv');
    }

    /** 中略 */

    /** @test */
    public function CSV検証_1列目_すべての値にクオーテーションを付ける()
    {
        $data = $this->extractedData;
        //1列目のカラム数は3であること
        $this->assertEquals(3, \count($data[0][0]));
        //値の検証
        $this->assertEquals('a,a', $data[0][0][0]);
        $this->assertEquals('b', $data[0][0][1]);
        $this->assertEquals('c', $data[0][0][2]);
    }

    /** @test */
    public function CSV検証_2列目_カンマの入った文字列にクオーテーションを付けない()
    {
        $data = $this->extractedData;
        //2列目のカラム数は3であること
        $this->assertEquals(3, \count($data[0][1]));
        //値の検証
        $this->assertEquals('a,a', $data[0][1][0]);
        $this->assertEquals('b', $data[0][1][1]);
        $this->assertEquals('c', $data[0][1][2]);
    }

    /** @test */
    public function CSV検証_3列目_カンマありのデータにだけクオーテーションを付ける
    {
        $data = $this->extractedData;
        //カラム数は3であること
        $this->assertEquals(3, \count($data[0][2]));
        //値の検証
        $this->assertEquals('a,a', $data[0][2][0]);
        $this->assertEquals('b', $data[0][2][1]);
        $this->assertEquals('c', $data[0][2][2]);
    }
}
テスト結果.txt
FFF.                                                                4 / 4 (100%)

Time: 6.49 seconds, Memory: 16.00 MB

There were 3 failures:

1) Tests\Unit\CSVExtractTest::CSV検証_1列目_すべての値にクオーテーションを付ける
Failed asserting that 4 matches expected 3.

/home/vagrant/code/ttn-1129-laravel/tests/Unit/CSVExtractTest.php:23

2) Tests\Unit\CSVExtractTest::CSV検証_2列目_カンマの入った文字列にクオーテーションを付けない
Failed asserting that 4 matches expected 3.

/home/vagrant/code/ttn-1129-laravel/tests/Unit/CSVExtractTest.php:35

3) Tests\Unit\CSVExtractTest::CSV検証_3列目_カンマありのデータにだけクオーテーションを付ける
Failed asserting that 4 matches expected 3.

/home/vagrant/code/ttn-1129-laravel/tests/Unit/CSVExtractTest.php:47

FAILURES!
Tests: 4, Assertions: 12, Failures: 3.

予想よりもエラーが多かったのですが、arrayのカラム数はファイル内最大のカラム数となるようでした。
カラム数のassertをコメントアウトして実行したところ、予想通り、取得される値は2列目が不正となり、1列目、3列目は有効でした。

テスト結果.txt
PHPUnit 7.5.13 by Sebastian Bergmann and contributors.

.F..                                                                4 / 4 (100%)

Time: 6.23 seconds, Memory: 16.00 MB

There was 1 failure:

1) Tests\Unit\CSVExtractTest::CSV検証_2列目_カンマの入った文字列にクオーテーションを付けない
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'a,a'
+'a'

/home/vagrant/code/ttn-1129-laravel/tests/Unit/CSVExtractTest.php:37

検証3:数値リテラル(整数、浮動小数点数)の検証

case3.csv
1,-2147483648,2147483647
1.0,2.231224,00030141.213
tests/Unit/CSVExtractTest.php
<?php
namespace Tests\Unit;
use Excel;
use App\Import\DummyImport;
use Tests\TestCase;

class CSVExtractTest extends TestCase
{
    /** 中略 */

    public function CSV検証_整数浮動小数点数()
    {
        $data = Excel::toArray(new DummyImport(), base_path().'/case3.csv');
        $this->assertEquals(2, \count($data[0]));

        //整数
        $this->assertEquals(3, \count($data[0][0]));
        $this->assertEquals(1, $data[0][0][0]);
        $this->assertEquals(-2147483648, $data[0][0][1]);
        $this->assertEquals(2147483647, $data[0][0][2]);
        //浮動小数点数
        $this->assertEquals(3, \count($data[0][1]));
        $this->assertSame(1.0, $data[0][1][0]);
        $this->assertSame(2.231224, $data[0][1][1]);
        $this->assertSame(30141.213, $data[0][1][2]);
    }
}

テスト結果.txt
PHPUnit 7.5.13 by Sebastian Bergmann and contributors.

...F                                                                4 / 4 (100%)

Time: 5.61 seconds, Memory: 16.00 MB

There was 1 failure:

1) Tests\Unit\CSVExtractTest::CSV検証_整数、浮動小数点数
Failed asserting that '00030141.213' is identical to 30141.213.

/home/vagrant/code/ttn-1129-laravel/tests/Unit/CSVExtractTest.php:88

FAILURES!
Tests: 4, Assertions: 24, Failures: 1.

00030141.213は文字列として扱うようでした。

検証4:特殊文字を含む場合

エスケープで使うバックスラッシュ\などを含んだ文字列でcsvファイルから値を取得します。

case4.csv
@#@!@$!#$,^^^^^32143()_----,\\++====51`5
tests/Unit/CSVExtractTest.php
<?php
namespace Tests\Unit;
use Excel;
use App\Import\DummyImport;
use Tests\TestCase;

class CSVExtractTest extends TestCase
{
    /** 中略 */

    /** @test */
    public function CSV検証_特殊文字を含む場合()
    {
        $data = Excel::toArray(new DummyImport(), base_path().'/case4.csv');
        $this->assertEquals(1, \count($data[0]));

        //整数
        $this->assertEquals(3, \count($data[0][0]));
        $this->assertEquals('@#@!@$!#$', $data[0][0][0]);
        $this->assertEquals('^^^^^32143()_----', $data[0][0][1]);
        $this->assertEquals('\\\\++====51`5', $data[0][0][2]);
    }
}
テスト結果.txt
PHPUnit 7.5.13 by Sebastian Bergmann and contributors.

.....                                                               5 / 5 (100%)

Time: 5.96 seconds, Memory: 16.00 MB

OK (5 tests, 29 assertions)

バックスラッシュ\だけ、php側で、エスケープをちゃんとすることで検証できるようでした。

参照

Laravel/Excelのインストール等に関しましては @rito328 さんのこちらのサイトに詳しく書いて頂いておりましたので参考にさせて頂きました。
https://www.ritolab.com/entry/160

以上です。

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