20210113のPHPに関する記事は19件です。

PHPでの「0」と「NULL」の比較について

いろんなところで言われてますがPHPでの0NULLの比較には注意が必要です。(ハマった)

$hoge = 0;
var_dump($hoge == NULL); // bool(true)
var_dump($hoge === NULL); // bool(false)
var_dump(is_null($hoge)); // bool(false)

is_null()、もしくは型比較までやったほうが安全という学びでした。

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

Google reCAPTCHAをフォームに設置する

スクリーンショット 2021-01-13 23.33.45.jpg

これを実装したくて、調べて、テストしてました。
忘れそうなので、備忘録も兼ねて記事にします。

reCAPTCHAのAPIキーを取得する

1) reCAPTCHAにログイン

reCAPTCHAのサイトにアクセスして、Googleアカウントでログイン。

2) サイトの登録

各種設定を行い、[作成]をクリックします。
reCAPTCHA-register.jpg

項目 設定内容
ラベル 覚えやすい名前を入力してください
reCAPTCHAタイプ 使いたいものを選択してください
ドメイン 設置するサイトのドメインを入力してください
オーナー メールアドレスが正しいか確認してください
reCAPTCHAに同意する チェックをONにします
アラートをオーナーに送信する ONにします

3) APIキーを取得する

APIキー画面が表示されたら、それぞれコピーしておきましょう。
また、シークレットキーは絶対に流出しないようにしましょう。

スクリーンショット 2021-01-13 22.21.35.jpg

管理画面>[設定]アイコン>[reCAPTCHAのキー]で後から確認することも可能です。

HTMLに設置する

設置したい箇所に<div class="g-recaptcha" data-sitekey="site key"></div>を入力します。
site keyにはAPIキーのサイトキーに上書きしてください。

contact.html
<form action="sample.php" method="post">
    <!-- 省略 -->
    <div class="g-recaptcha" data-sitekey="site key"></div>
    <input name="submit_buton" type="submit" id="submit_button" value="送信" class="fas">
</form>

設置はこれで完了です。

PHPで動作を記述する

サーバー側で、reCAPTCHAのチェックがONになっているか判定するコードを書く必要があります。

今回は、formを実行したときに呼び出すphpファイルの中に、以下のコードを追記してみました。
secret=secretKeysecretKey部分を、取得したシークレットキーに上書きしてください。

sample.php
<?php
$captcha;
if (isset($_POST['g-recaptcha-response'])) {
   $captcha = $_POST['g-recaptcha-response'];
}
if (!$captcha) {
   $alert = "<script type='text/javascript'>alert('reCAPTCHA にチェックを入れて下さい。');</script>";
   echo $alert;
   echo "<script type='text/javascript'>history.go(-1);</script>";
   exit;
}
$response = file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret=secretKey&response=" . $captcha . "&remoteip=" . $_SERVER['REMOTE_ADDR']);

// reCAPTCHAが成功した場合の処理を以降に記述
// (例: お問い合わせ送信処理、投稿処理 等)

?>

チェックがONになっていない場合、
alert('reCAPTCHA にチェックを入れて下さい。');";でブラウザの通知を使ってチェックを促し、history.go(-1);で自動で前のページに戻す。という処理にしています。

また、JavaScriptを使うため、$alert = "<script type='text/javascript'>~~~</script>";と記述して、タグの中にJavaScriptコードを記述しています。

実行

チェックがOFFのままフォームを送信すると、通知ウィンドウが正しく表示されました。
スクリーンショット 2021-01-13 23.23.59.jpg

また、チェックがONのときは正常に送信されることを確認しました。

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

Golang製PHPアプリケーションサーバRoadRunnerでLaravel-Adminを動かす

アプリケーションサーバーの新潮流

昨今のPHP界隈では、Swooleに代表されるように、よくあるApache+mod_phpやPHP-FPMによる従来の構成と異なる実行方法を持つPHPのためのHTTPサーバまたはアプリケーションサーバが少しずつ注目を集めるようになっています。

RoadRunner

RoadRunnerもそんな新興のアプリケーションサーバの1つで、Go言語で書かれています。 HTTPのリクエストを前段のGoのHTTPハンドラがさばき、ロードバランサ/プロセスマネージャがPHPのWorkerにリクエストを割り振るという構成になっています。

通常のHTTPサーバだけでなく、RoadRunnerによるgRPCサーバの実装も公開されており、PHPの新しい可能性を見せてくれています。

RoadRunnerを試す

RoadRunnerはLaravelもサーポートしており、Laravel用のパッケージも開発されているので

今回はこれをベースにRoadRunner実装していきたいと思います。

RoadRunnerのインストール

まずは下記コマンドでパッケージのインストールを行います。

# roadrunner
composer require spiral/roadrunner "^1.8"
# roadrunner-laravel
composer require spiral/roadrunner-laravel "^3.4"

次に、下記コマンドを実行してパッケージ構成ファイル(./config/roadrunner.php)を作ります。

php artisan vendor:publish --provider='Spiral\RoadRunnerLaravel\ServiceProvider' --tag=config

.rr.ymlの作成

roadrunnerの設定ymlファイルを書きます。

80番ポートで起動するように設定します。

env:
  APP_REFRESH: true
http:
  address: 0.0.0.0:80
  http2:
    enabled: true
    h2c: true
    maxConcurrentStreams: 128
  workers:
    command: 'php ./vendor/bin/rr-worker'
static:
  dir: public
reload:
  interval: 1s
  patterns: [ '.php' ]
  services:
    http:
      dirs: [ '' ]
      recursive: true

諸々の微修正

logging.php

'stdout' => [
    'driver' => 'monolog',
    'handler' => StreamHandler::class,
    'with' => [
        //php://stdoutにするとエラーになってコケるのでphp://stderrに変える
        'stream' => 'php://stderr',
     ],
     'level' => 'debug',
],

composer.jsonにscriptを追加する。

"scripts": {
        "roadrunner:dev": [
            "composer dump-autoload",
            "./rr serve -v -d"
        ],
        "roadrunner:prod": [
            "./rr serve"
        ],
        "roadrunner:setup": [
            "rr get-binary"
        ],
    }

startup.shに起動コマンドを乗せる

# roadrunner
composer roadrunner:setup
chmod +x rr
composer roadrunner:dev

これで大体の設定は完了しました。

起動してみる

以下の手順で起動させます。

cp docker-compose.example.yml docker-compose.yml

docker-compose up -d

無事に起動できました。

スクリーンショット 2020-11-27 12.59.30.png (168.6 kB)

まとめ

今回はGolangで作られたPHPのweb・アプリケーションサーバーを使った。サンプルを作ってみました。
おもしろいですが、不安定なのでプロダクション環境ではあまり使うのはおすすめできないですね。

参考文献

https://nextat.co.jp/staff/archives/235

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

Golang製PHPアプリケーションサーバRoadRunnerでLaravel-adminを動かす

アプリケーションサーバーの新潮流

昨今のPHP界隈では、Swooleに代表されるように、よくあるApache+mod_phpやPHP-FPMによる従来の構成と異なる実行方法を持つPHPのためのHTTPサーバまたはアプリケーションサーバが少しずつ注目を集めるようになっています。

RoadRunner

RoadRunnerもそんな新興のアプリケーションサーバの1つで、Go言語で書かれています。 HTTPのリクエストを前段のGoのHTTPハンドラがさばき、ロードバランサ/プロセスマネージャがPHPのWorkerにリクエストを割り振るという構成になっています。

通常のHTTPサーバだけでなく、RoadRunnerによるgRPCサーバの実装も公開されており、PHPの新しい可能性を見せてくれています。

RoadRunnerを試す

RoadRunnerはLaravelもサーポートしており、Laravel用のパッケージも開発されているので

今回はこれをベースにRoadRunner実装していきたいと思います。

RoadRunnerのインストール

まずは下記コマンドでパッケージのインストールを行います。

# roadrunner
composer require spiral/roadrunner "^1.8"
# roadrunner-laravel
composer require spiral/roadrunner-laravel "^3.4"

次に、下記コマンドを実行してパッケージ構成ファイル(./config/roadrunner.php)を作ります。

php artisan vendor:publish --provider='Spiral\RoadRunnerLaravel\ServiceProvider' --tag=config

.rr.ymlの作成

roadrunnerの設定ymlファイルを書きます。

80番ポートで起動するように設定します。

env:
  APP_REFRESH: true
http:
  address: 0.0.0.0:80
  http2:
    enabled: true
    h2c: true
    maxConcurrentStreams: 128
  workers:
    command: 'php ./vendor/bin/rr-worker'
static:
  dir: public
reload:
  interval: 1s
  patterns: [ '.php' ]
  services:
    http:
      dirs: [ '' ]
      recursive: true

諸々の微修正

logging.php

'stdout' => [
    'driver' => 'monolog',
    'handler' => StreamHandler::class,
    'with' => [
        //php://stdoutにするとエラーになってコケるのでphp://stderrに変える
        'stream' => 'php://stderr',
     ],
     'level' => 'debug',
],

composer.jsonにscriptを追加する。

"scripts": {
        "roadrunner:dev": [
            "composer dump-autoload",
            "./rr serve -v -d"
        ],
        "roadrunner:prod": [
            "./rr serve"
        ],
        "roadrunner:setup": [
            "rr get-binary"
        ],
    }

startup.shに起動コマンドを乗せる

# roadrunner
composer roadrunner:setup
chmod +x rr
composer roadrunner:dev

これで大体の設定は完了しました。

起動してみる

以下の手順で起動させます。

cp docker-compose.example.yml docker-compose.yml

docker-compose up -d

無事に起動できました。

スクリーンショット 2020-11-27 12.59.30.png (168.6 kB)

まとめ

今回はGolangで作られたPHPのweb・アプリケーションサーバーを使った。サンプルを作ってみました。
おもしろいですが、不安定なのでプロダクション環境ではあまり使うのはおすすめできないですね。

参考文献

https://nextat.co.jp/staff/archives/235

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

Golang製PHPアプリケーションサーバRoadRunnerを試す

アプリケーションサーバーの新潮流

昨今のPHP界隈では、Swooleに代表されるように、よくあるApache+mod_phpやPHP-FPMによる従来の構成と異なる実行方法を持つPHPのためのHTTPサーバまたはアプリケーションサーバが少しずつ注目を集めるようになっています。

RoadRunner

RoadRunnerもそんな新興のアプリケーションサーバの1つで、Go言語で書かれています。 HTTPのリクエストを前段のGoのHTTPハンドラがさばき、ロードバランサ/プロセスマネージャがPHPのWorkerにリクエストを割り振るという構成になっています。

通常のHTTPサーバだけでなく、RoadRunnerによるgRPCサーバの実装も公開されており、PHPの新しい可能性を見せてくれています。

RoadRunnerを試す

RoadRunnerはLaravelもサーポートしており、Laravel用のパッケージも開発されているので

今回はこれをベースにRoadRunner実装していきたいと思います。

RoadRunnerのインストール

まずは下記コマンドでパッケージのインストールを行います。

# roadrunner
composer require spiral/roadrunner "^1.8"
# roadrunner-laravel
composer require spiral/roadrunner-laravel "^3.4"

次に、下記コマンドを実行してパッケージ構成ファイル(./config/roadrunner.php)を作ります。

php artisan vendor:publish --provider='Spiral\RoadRunnerLaravel\ServiceProvider' --tag=config

.rr.ymlの作成

roadrunnerの設定ymlファイルを書きます。

80番ポートで起動するように設定します。

env:
  APP_REFRESH: true
http:
  address: 0.0.0.0:80
  http2:
    enabled: true
    h2c: true
    maxConcurrentStreams: 128
  workers:
    command: 'php ./vendor/bin/rr-worker'
static:
  dir: public
reload:
  interval: 1s
  patterns: [ '.php' ]
  services:
    http:
      dirs: [ '' ]
      recursive: true

諸々の微修正

logging.php

'stdout' => [
    'driver' => 'monolog',
    'handler' => StreamHandler::class,
    'with' => [
        //php://stdoutにするとエラーになってコケるのでphp://stderrに変える
        'stream' => 'php://stderr',
     ],
     'level' => 'debug',
],

composer.jsonにscriptを追加する。

"scripts": {
        "roadrunner:dev": [
            "composer dump-autoload",
            "./rr serve -v -d"
        ],
        "roadrunner:prod": [
            "./rr serve"
        ],
        "roadrunner:setup": [
            "rr get-binary"
        ],
    }

startup.shに起動コマンドを乗せる

# roadrunner
composer roadrunner:setup
chmod +x rr
composer roadrunner:dev

これで大体の設定は完了しました。

起動してみる

以下の手順で起動させます。

cp docker-compose.example.yml docker-compose.yml

docker-compose up -d

無事に起動できました。

スクリーンショット 2020-11-27 12.59.30.png (168.6 kB)

まとめ

今回はGolangで作られたPHPのweb・アプリケーションサーバーを使った。サンプルを作ってみました。
おもしろいですが、不安定なのでプロダクション環境ではあまり使うのはおすすめできないですね。

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

Laravelで手軽にログイン認証機能を実装する

【前提条件】
Lamp環境が正常にインストール出来ている。
Node.jsのインストールが出来ている。
データ・ベースの設定が出来ている。
artisanコマンドがある程度扱える。

Node.jsがインストール出来ていない方は@seiba様の記事を参照して下さい
【引用リンク】
https://qiita.com/seibe/items/36cef7df85fe2cefa3ea

1、バージョンを確認する

まずはLaravelのバージョンを確認しましょう、以下のコマンドを入力して下さい。

php artisan --version

Laravelのバージョンが6系か7系かを確認します。

2、Laravel/uiをインストールする。

6系の場合は以下のコマンドでインストール

composer require laravel/ui:^1.0 --dev

7系の場合は以下のコマンドでインストール

composer require laravel/ui:^2.4

3、認証機能を実装する

6系、7系共通で以下のコマンドを入力してログイン機能を実装する

php artisan ui vue --auth

この時点ではログイン機能は完成していません、この時点でREGISTER場面を
見てみると………フロントエンド部分が実装されていません。
authtest.png

4、フロントエンド部分を実装する〜完成

npmをインストールする

npm install

npmをビルドする

npm run dev

最期に忘れずにmigrationコマンドででログイン情報を格納するテーブルを作成する。

php artisan migration

完成です
Laravelを立ち上げてみて右端のRegisterをクリックしてみる
authtest1.png
すると先程は実装されていなかったフロントエンド部分の実装がなされている。
authtest2.png
実際に登録してみる
authtest3.png

実際にログインに成功するとログイン後のページに遷移できる。
authtest4.png

番外編:どのようにデータが格納されているか

実際にパスワードが格納されているテーブルを見てみるとパスワードが暗号化されて格納されており、手軽に実装出来てかなり高度な認証機能である事がわかる。

|  1 | ララベルユーザー       | laravel@gmail.com | NULL |
 $2y$10$KPPRJJH0fOYl1JocEdQscu3NNl5BrJU.MpEu4AY1I2ye6F5z18Zsq | NULL           | 2021-01-13 12:40:02 | 2021-01-13 12:40:02 |
+----+--------------------+-------------------+-------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+

参考サイト
【公式版】Laravel公式チュートリアル日本語訳
https://readouble.com/laravel/7.x/ja/authentication.html

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

"Hello World"でみるプログラミング言語

はじめに

プログラミングを学ぶものなら誰しも通る道、Hello world。今回はそんなHello worldを出力するためのプログラムで、5つのプログラミング言語を比較していきます。皆さんが使っていなそうな言語を選びました。が、あくまでも独断と偏見によって決定されています。

1.C

いわずと知れた言語ですね。

#include <stdio.h>
int main(void) {
    printf("Hello world\n");
}

2.Python

簡潔に書けることが利点ですね。

print("Hello world")

3.Ruby

日本生まれの言語ですね。

puts "Hello world"

4.FORTRAN

最初の高水準言語として有名ですね。まだ息してたのか...

program hello
  print *, 'Hello world'
end program hello

5. PHP

HTML/CSSと一緒に使われたりしますね。

<?php
  echo "Hello world\n";
?>

終わりに

以上です。いかがでしたでしょうか?Rubyはやっぱり短かったですね。それが長所ですよね。PHPechoてエコーって読むんですね。初めて知りました。若輩者ゆえ、もしかしたら間違いがあるかもしれないので、その時はコメントなんかで教えてください。

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

Docker x Laravel 脅威の1ファイル!最小構成でローカル開発環境を構築する

docker-compose.yml の1ファイル、ウェブサーバーコンテナ、データベースサーバーコンテナなしで作成する最小構成のDocker Laravel開発環境です。

環境

  • PHP: 7.4
  • Composer: 2.0
  • Docker: 20.10.2
  • Docker Compose: 1.27.4

docker-compose.yml 1ファイルのみ

docker-compose.yml

docker-compose.yml
version: "3.8"
services:
  composer:
    image: composer:2.0
    volumes:
      - ./backend:/work/backend
    working_dir: /work/backend

  app:
    image: php:7.4-cli-buster
    ports:
      - 8000:8000
    volumes:
      - ./backend:/work/backend
    working_dir: /work/backend
    command: php artisan serve --host=0.0.0.0

使い方

Laravelプロジェクトの作成

$ docker-compose run composer create-project --prefer-dist laravel/laravel .

# Laravelバージョン指定
$ docker-compose run composer create-project --prefer-dist "laravel/laravel=6.*" .

Laravelビルトインサーバーの起動

$ docker-compose up -d app

http://localhost:8000

こだわったポイント

Dockerfileのビルドが必要ないこと。
もはやカップ麺を作るより速く、お湯を沸かす時間だけで作れてしまうほどシンプルな構成です。

作った理由

Dockerfileのビルドの時間がかかるから。
Laravelのお試し環境を作るのに最強の構成を作るまででもないなという時。

GitHub

オススメの記事

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

【Laravel】コントローラーとは?作成や編集方法を実例で解説。

Laravelのコントローラーについて。実際にコントローラーを作成してブラウザに表示させることで理解を深める。

目次

  1. コントローラーとは?
  2. コントローラーの作成方法
  3. コントローラーの保存場所
  4. コントローラーの中身
  5. コントローラーの作成例
  6. コントローラー呼び出し時の注意点
  7. コントローラーでviewファイルを呼び出す


コントローラーとは?

Laravelのコントローラーでは、クラスを作成しメソッドを定義することができる。

ルーティングファイル(web.php)などにメソッドを記述していくとファイル長が膨大になってしまうため、コントローラーを使用することで、メソッドを利用しやすくなる。

コントローラーの作成方法

$ php artisan make:controller コントローラー名

コントローラー名は冒頭大文字のキャメル表記。

$ php artisan make:controller HelloController

Controller created successfully.


コントローラーの保存場所

artisanコマンドを実行すると、app/Http/Controllers内に新たにファイルが生成される。

image.png

image.png

専用のディレクトリの中に入れたい場合

コントローラー名の前にディレクトリ名を付ければOK。

例: php artisan make:controller Test/HelloController

image.png


コントローラーの中身

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class HelloController extends Controller
{
    //
}

namespace App\Http\Controllers;
名前空間。

app/Http/Controllers配下にファイルがあるため、そのまま名前空間として指定。

名前空間をつけることで、同じメソッド名でも名前空間が異なればそれぞれ別のメソッドとして使うことができる。

use 名前空間
指定した名前空間をインポート。

use Illuminate\Http\Request;
LaravelのAPIである、名前空間Illuminate\Http\Requestをインポートしている。
これで、Request.phpに定義されているメソッドが使えるようになる。

▼Illuminate\Http\Requestの場所
vender > laravel > framework > src > Illuminate > Http > Request

Requestメソッド一覧



class HelloController extends Controller
定義したクラス名HelloControllerに、親クラスとして、Controllerクラスを継承している。

Controllerクラスには、validate()middleware()など便利なメソッドが定義されている。

{ // }
ここに、メソッドを記述していく。


コントローラーの作成例

step1. メソッドを定義

//の中に以下を追記

public function message(){
        $msg = 'こんにちは';
        return $msg;
    }

messageメソッドを呼び出すと、「こんにちは」を返す。

HelloController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class HelloController extends Controller
{
    public function message(){
        $msg = 'こんにちは';
        return $msg;
    }
}


step2. ルーティングで呼び出す

web.php
Route::get('msg', 'App\Http\Controllers\HelloController@message');

URIがmsgにアクセスしたら、HelloControllerのmessageメソッドを実行する。

以上で完了。



▼ブラウザの表示

image.png


コントローラー指定時の注意点

Laravel7までは、App\Http\Controllers\が省略できたが、Laravel8からは省略するとエラーが表示されるようになった。

対策として完全な名前空間で指定した。

他にも、Laravel7の環境と同じにする方法や、アクション構文を使用する方法がある。


Laravel7と同じ環境にする

以下ファイルのコメントアウトを解除する。
app > Providers > RouteServiceProvider.php

image.png

解除するコメントアウト
//protected $namespace = 'App\\Http\\Controllers';
RouteServiceProvider.php
    protected $namespace = 'App\\Http\\Controllers';  //コメントアウトを解除

    /**
     * Define your route model bindings, pattern filters, etc.
     *
     * @return void
     */
    public function boot()
    {
        $this->configureRateLimiting();

        $this->routes(function () {
            Route::prefix('api')
                ->middleware('api')
                ->namespace($this->namespace)
                ->group(base_path('routes/api.php'));

            Route::middleware('web')
                ->namespace($this->namespace)
                ->group(base_path('routes/web.php'));
        });
    }



ルーティングのコントローラー名を簡略化して完了。

web.php
Route::get('msg', 'HelloController@message');


アクション構文を使用する

ルーティングファイルの冒頭で、useを使ってHelloControllerクラスをインポートする。

use App\Http\Controllers\HelloController;

getメソッドの第二引数で以下を指定。
[コントローラー名::class, 'メソッド名']

web.php
use Illuminate\Support\Facades\Route;  //デフォルトで記述
use App\Http\Controllers\HelloController; //追記

Route::get('msg', [HelloController::class, 'message']);


コントローラーでviewファイルを呼び出す

コントローラーのメソッドが呼び出されると、viewファイルを描画するプログラムを作成する。

step1. コントローラーの作成

HelloController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;

class HelloController extends Controller
{
    public function message(){
        $msg = 'こんにちは';
        $title = 'this is title!';

        return view('child', ['hello'=>$msg, 'title'=>$title]);
    }
}

HelloControllerのmessageメソッドを実行すると、child.blade.phpを表示する。

その際、helloという変数で$msgを、titleという変数で$titleを渡す。


step2. viewファイルの作成

ここでは、child.blade.phpと、レイアウトとなるapp.blade.phpを作成する。

image.png

app.blade.php
<html>
    <head>
        <title>@yield('title')</title>
    </head>
    <body>
        <div class="container">
            @yield('content')
        </div>
    </body>
</html>

親ビューとなるapp.blade.phpを作成。
@yieldで子ビューのセクションを呼び出している。


child.blade.php
@extends('layouts.app')

@section('title', $title)

@section('content')
    <p>{{ $hello }}</p>
@stop

@extendsで親ビューを継承。
@sectionでコントローラーから渡された変数を使用。


step3. ルーティングの作成

パス「xxx」にアクセスすると、HelloControllerのmessageメソッドを実行するようにルーティングファイルに記述。

web.php
Route::get('xxx', 'HelloController@message');

以上で完了。

>section, yield, extendの詳細はこちらを参照



▼ブラウザの表示

image.png

ページタイトルと、ページの内容に、コントローラーで定義した変数が表示されている。

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

【Laravel】Illuminateとは何か?ファイルはどこにあるのか?

Laravelでコントローラーを作成した場合などに、use Illuminate\Http\Request;といった記述がある。

これは名前空間のIlluminate\Http\Requestをインポートしているということだが、そもそもIlluminateとはどこにあり、何なのか?

Illuminateはどこにあるのか?

Illuminateは、laravelでプロジェクトを作成した時に自動生成される、vendorディレクトリ配下に存在する。

vender > laravel > framework > src > Illuminate

image.png

vendorディレクトリはcomposerと依存関係にある。

Illuminateとは?

IlluminateはLaravelのコンポーネントが入っている。
Illuminate\Http\Requestクラスの、hasメソッドのように、各クラスとメソッドが定義されている。

Laravelでデフォルトで使えるありがたいメソッドがたくさんあるのは理解できたが、その名前とマッチしなさすぎる、、

IlluminateはLaravel4のコードネームだそうで、このタイミングでcomposerを通じてframeworkをインストールするように認め、それを記念して(?)Illuminateというフォルダに格納されているらしい。(歴史を知らない人にはとにかくわかりにくい!!)

まぁ、Laravelの大事なコンポーネントが置いてある場所ということ。


・参考
Illuminateメソッド一覧

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

curl error 60を解決した

エラーが出ていた元のコード

//$postと$headersについては省略
$ch = curl_init('URL');
        $options = [
            CURLOPT_CUSTOMREQUEST => 'POST',
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_BINARYTRANSFER => true,
            CURLOPT_HEADER => true,
            CURLOPT_POSTFIELDS => $post,
        ];
        curl_setopt_array($ch, $options);

        $result = curl_exec($ch);

エラーが出なくなったコード

$ch = curl_init('URL');
        $options = [
            CURLOPT_CUSTOMREQUEST => 'POST',
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_BINARYTRANSFER => true,
            CURLOPT_HEADER => true,
            CURLOPT_POSTFIELDS => $post,
            CURLOPT_SSL_VERIFYPEER => false,  //追加
            CURLOPT_VERBOSE => true,          //追加
        ];
        curl_setopt_array($ch, $options);

        $result = curl_exec($ch);

参考(こちらのラインプッシュを試した際に出くわしたエラーです):https://imoni.net/tips/00xx/0012.html

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

WordPressで検索機能を自作する

ワードプレスのテーマtwentytwentyoneを基盤に、検索機能を自作する実装を行ったので、備忘録的にこの記事を残す。

環境情報

PHP:version 7.3.12
WordPress:version 5.6
WPテーマ:twentytwentyone

仕様

・検索対象の投稿タイプは search のみとする。

・フリーワードの検索対象は、タイトルと(本文と備考と)繰り返しフィールドの search_other_value の値の二つ

・「さらに条件を絞り込む」に指定があればキーワード検索と AND で繋ぎ絞り込む

・「さらに条件を絞り込む」の項目は、一つのブロック内は OR で繋ぎ、異なるブロック同士は AND で繋ぐ。

・地域の設定はカスタムタクソノミーでカテゴリーを作っているため、選択した地域名に該当するカテゴリーに属する会社名を検索に引っ掛ける。

・車種の登録はそれぞれ車種ごとに入力フィールド分かれており、台数が値として入っているため、「さらに条件を絞り込む」で任意の車種にチェックが入っていた場合には、その車種の台数が1以上の会社名を検索に引っ掛ける。

・対応言語の登録は、一つの入力フィールドでラジオボタンで登録する言語を選択しているため、配列の形で値が格納されている。そのため、「さらに条件を絞り込む」で任意の言語にチェックが入っていた場合には、その言語を配列に含む会社名を検索に引っ掛ける。

実装

検索ページ

今回は検索で表示させたい投稿の投稿タイプ名が search というもの一つだけだったので、検索ページを search のアーカイブページに設置しました。

archive-search.php
<div class="search-box">
  <form role="search" method="get" id="searchform" action="/" >
    <input type="text" name="s" id="search-input" placeholder="○○内を検索" />
    <div>
      <label>さらに条件を絞り込む</label>
      <div>
        <label>エリア</label>
        <select name="category" style="margin-top:20px;">
          <option value="" selected>地域を選択</option>
          <?php
            $terms = get_terms( 'search_category',
              array(
                'parent' => 0,
                'orderby' => 'id',
                'order' => 'ASC'
              )
            );
            foreach ( $terms as $term ) :
          ?>
          <option value="<?php echo $term->slug; ?>"><?php echo $term->name; ?></option>
          <?php endforeach; ?>
        </select>
      </div>

      <div>
        <label>こだわり</label><br>
        <label><input type="checkbox" name="carmodel[]" value="general">一般タクシー</label><br>
        <label><input type="checkbox" name="carmodel[]" value="higher">ハイヤー</label><br>
        <label><input type="checkbox" name="carmodel[]" value="jumbo">ジャンボ</label><br>
        <label><input type="checkbox" name="carmodel[]" value="welfare">福祉</label><br>
      </div>

      <div>
        <label>外国語対応</label><br>
        <label><input type="checkbox" name="language[]" value="english">英語</label><br>
        <label><input type="checkbox" name="language[]" value="chinese">中国語</label><br>
        <label><input type="checkbox" name="language[]" value="korean">韓国語</label><br>
      </div>
    </div>

    <input type="hidden" name="post_type" value="search">
    <button type="submit" accesskey="f" class="search-btn">検索</button>
  </form>
</div>

functions.php での処理

functions.php
//======================================================================
// 検索機能
//======================================================================

// 検索時の表示ファイルの設定
function custom_search_template( $template ) {
  if ( is_search() ) {
    $post_types = get_query_var( 'post_type' );
    foreach ( ( array ) $post_types as $post_type )
      $templates[] = "search-{$post_type}.php";
      $templates[] = 'search.php';
      $template = get_query_template( 'search', $templates );
     }
  return $template;
}
add_filter( 'template_include', 'custom_search_template' );

// キーワード検索に特定のカスタムフィールドを追加
function custom_search($search, $wp_query) {
    global $wpdb;

    if (!$wp_query->is_search) return $search;
    if (!isset($wp_query->query_vars)) return $search;

    $search_words = explode(' ', isset($wp_query->query_vars['s']) ? $wp_query->query_vars['s'] : '');
    if ( count($search_words) > 0 ) {
        $search = '';
        $search .= "AND post_type = 'search'"; // 投稿タイプ名を設定
        for ( $i = 0; $i <= 9; $i++ ) {
            $custom_field_array[] = '"search_other_' . $i . '_search_other_value"';
        }
        $custom_field_list = implode( ', ', $custom_field_array );
        foreach ( $search_words as $word ) {
            if ( !empty($word) ) {
                $search_word = '%' . esc_sql( $word ) . '%';
                $search .= " AND (
                    {$wpdb->posts}.post_title LIKE '{$search_word}'
                    OR {$wpdb->posts}.post_content LIKE '{$search_word}'
                    OR {$wpdb->posts}.ID IN (
                        SELECT distinct post_id
                        FROM {$wpdb->postmeta}
                        WHERE {$wpdb->postmeta}.meta_key IN ( 
                            {$custom_field_list}
                        ) AND meta_value LIKE '{$search_word}'
                    )
                ) "; // INのカッコ内に対象としたいフィールド名をコンマ区切りで追加
            }
        }
    }
    return $search;
}
add_filter('posts_search','custom_search', 10, 2);

// 検索結果ページのメインクエリを変更
function change_search_main_query( $query ) {
    // 指定したページとメインクエリの場合に限定
    if ( $query->is_main_query() && ( $query->is_search() || isset( $_GET['category'] ) || isset( $_GET['carmodel'] ) || isset( $_GET['language'] ) ) ) {
        $s = $_GET['s'];
        $category = $_GET['category'];
        $carmodel = $_GET['carmodel'];
        $language = $_GET['language'];
        // 地域
        if ( $category ) {
            $taxquerysp[] = array(
                'taxonomy'         => 'search_category',
                'terms'            => $category,
                'include_children' => false,
                'field'            => 'slug',
                'operator'         => 'AND'
            );
        }

        $metaquerysp = [
            'relation' => 'AND',
        ];
        // 車種
        if ( $carmodel ) {
            $sub_metaquerysp = [
                'relation' => 'OR',
            ];
            foreach ( $carmodel as $val ) {
                $sub_metaquerysp[] = array(
                    'key'     => 'search_' . $val,
                    'value'   => 0,
                    'compare' => '>'
                );
            }
        }
        // 言語
        if ( $language ) {
            $sub_metaquerysp = [
                'relation' => 'OR',
            ];
            foreach ( $language as $val ) {
                $sub_metaquerysp[] = array(
                    'key'     => 'search_language',
                    'value'   => $val,
                    'compare' => 'LIKE',
                );
            }
        }
        $metaquerysp[] = $sub_metaquerysp;

        $query->set( 'post_type', 'search' );
        $query->set( 'tax_query', $taxquerysp );
        $query->set( 'meta_query', $metaquerysp );
        $query->set( 's', $s );
    }
}
add_action( 'pre_get_posts', 'change_search_main_query' );

検索結果ページ

search-search.php
<?php
if ( have_posts() ) :
    while ( have_posts() ) : the_post();
?>
    <!-- ループ内の処理を記述 -->
<?php
    endwhile;
else :
?>
    該当なし
<?php
endif;
?>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP 投稿画面でリロードすると再度登録されてしまう

環境
MySQL 5.7.30
PHP 7.3.21

現状
投稿画面で投稿後、リロードすると投稿した内容が再度登録されてしまう。

ヒントになった事

var_dump();を投稿画面のファイルに記入

GETが表示される

なので、POSTした時に何か対処することに。

解決

投稿画面(board.php)で読み込んでいる、PHPファイル(create.php)に

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $board = [
        'nickname' => $_POST['nickname'],
        'message' => $_POST['message'],
    ];
    header('Location: board.php');
}

送信したとき、POSTだったら投稿画面(board.php)を読み込むとすれば完了!

参考記事
【PHP】PHPでリロード対策をして二重投稿を防止しよう
https://the-zombis.sakura.ne.jp/wp/blog/2014/07/11/post-2139/

後、数えきれないぐらい関連記事を参考にしました笑
そして、他のやり方もあります!

課題は入力値が残らなくなってしまったのでどこかのタイミングで直します!

何かご指摘いただけたらありがたいです!

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

Laravelでサイトマップを分割する

Laravelium/laravel-sitemapを使う

インストール方法は以下
https://github.com/Laravelium/laravel-sitemap

サイトマップを分割する

サイトマップに関する設定はconfig/sitemap.phpでもできる。
今回は個別に設定するのでコンフィグファイルはいじらない。

use_limit_sizeはfalseのままにする。trueにするとURLが全件出力されないし、ファイル分割もされない。

public static function toXml($data){
  $sitemap = \App::make('sitemap');
  $sitemap->model->setMaxSize(1000); // 1xmlあたりのURL数
  foreach ($data as $d) {
    $sitemap->add(
      $d->url,
      $d->last_modified
      $d->priority,
      $d->frequency,
    );
  }
  $sitemap->store('xml', 'sample_sitemap', '/tmp');
  //** 出力結果例 **//
  // sample_sitemap.xml <= サイトマップインデックス
  // sample_sitemap_0.xml
  // sample_sitemap_1.xml
}

設定するだけで分割されるので便利。
しかもサイトマップ(子)をまとめるサイトマップインデックス(親)も作成してくれるので、ファイル数が多くなってもサイトマップインデックス(親)だけSearch Consoleに登録すれば良い。便利。

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

Laravel で画像サイトマップを作る

Laravelium/laravel-sitemapを使う

インストール方法は以下
https://github.com/Laravelium/laravel-sitemap

サンプル

add関数の第5引数が画像サイトマップ用の値。
公式には見当たらなかったのでメモ。

public function toXmlFile(){
  $sitemap = \App::make('sitemap');
  $sitemap->add(
    "http://sample.com/spot/1",  // page url
    "2021-01-12T00:00:00+00:00", // last modified
    0.6,                         // priority
    "yearly",                    // frequency
    [                            // 画像サイトマップの設定
      [
       'url' => "http://sample.com/spot/1.jpg",
       'title' => "サンプルタイトル",
       'caption' => "このサンプルは日本語",
       'geo_location' => "東京都、新宿区",
       'license' => "http://sample.com"
      ]
    ]
  );
}

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

[PHP]try-catch-finallyのfinally句で、syntax errorが出る原因

PHPのtry-catch-finally文の、finally句でsyntax errorが出た。
エラー内容は以下のような感じ。

PHP Parse error:  syntax error, unexpected '{' (以下省略)

パッと見間違ってないし、finally句を除くと処理がうまくいく。

調査を続けたところ、finally句は、PHP5.5より前のバージョンだと、使えないらしい。

対象サーバのPHPのバージョンを確認してみる

$ php -v
PHP 5.3.3 (cli) (built: Jul  9 2015 17:39:00)
(以下省略)

PHPのバージョンが5.3.3・・?
5.5より古いやないかい!

PHPのバージョンが7以上のサーバで実行したところ、うまくいきましたとさ!

結論

PHPのtry-catch-finallyのfinally句で、syntax errorが出た場合は、バージョンが5.5より前のバージョンでないか確認 & 該当の場合はfinally句は使わないようにすべし!

参考

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

PHPでファイルのダウンロード(またはアップロード)や外部APIへのリクエストを行うと、同一セッション内の他のリクエストが処理できなくなる問題への対応方法

PHPでファイルのダウンロードやアップロード、外部APIへのリクエストを行うと、それらの処理中に他のリクエストが処理できなくなる事象が発生する場合があります。

発生条件

  • ログインを必要とするシステムである。あるいは、ログインは行わないが、セッションの利用を行うシステムである。
  • ファイルをダウンロード(またはアップロード中)、あるいは外部APIへのリクエストを行っている。また、処理時間のかかる(長い)処理である。
  • 上記処理中に他の処理を行おうとすると(例えば同じシステム内の別機能を呼び出そうとすると)、結果が表示されるまでに時間がかかったり、タイムアウトする。
  • 上記処理が完了あるいは中断後は、正常に他の処理を呼び出せるようになる。
  • 上記処理中に、別ブラウザ(あるいは同一ブラウザのシークレットモード)から同じシステムへログインし、別機能を呼び出した場合は、当該事象(結果が表示されるまでに時間がかかる、タイムアウトする)は発生しない(=同一セッションでは発生するが、別セッションでは発生しない)。
  • セッションを利用しない機能では、当該事象は発生しない。

原因

セッションが排他ロックされているためです。
session_start() を呼び出すと、処理が完了されるか、明示的にセッションをクローズするまでの間、セッションがオープンされたままとなり、その間、排他ロックされます。

ほとんどの処理(すぐに完了する処理)においては、排他ロックによる影響を意識する必要はありませんが、ファイルのダウンロードやアップロード、あるいは外部APIへのリクエスト等、処理時間が長くなりがちな処理においては、排他ロックの影響について考慮する必要があります。

対応方法

ファイルのダウンロードやアップロード、時間のかかる外部APIへのリクエストを行う前に、セッションを明示的にクローズします。

※上記処理に必要となる値がセッションに格納されている場合は、セッションをクローズする前に取得し、変数や配列に格納しておきます。

session_write_close() を、時間のかかる処理の前に実行します。
参考:https://www.php.net/manual/ja/function.session-write-close.php

もし、処理完了後にセッションを参照したり、セッションデータを書き込みたい場合は、 処理完了後にsession_start()してセッションを再開してから、セッションデータの参照や書き込みを行います。
参考:https://www.php.net/manual/ja/function.session-start.php

なお、セッションをクローズすることによって、セッションデータがクリアされることはありません。

また、上記はいずれもPHP標準メソッドになります。フレームワークを利用している場合は、フレームワークごとに実装は異なりますので、フレームワークのドキュメントより関連する内容を確認して対応してください。

参考

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

PHP + Ajax(jQuery) + reCAPTCHA v3 を実装する

reCAPTCHA v3 とは、クローラーやロボットによるアクセスを制限して、無駄にサイトアクセスさせないために設置する認証装置です。

主に、連投されては困るようなコンタクトフォームや、コメント投稿、ユーザー登録などの画面に設置して、そのボタンを押すのが人間なのかどうかを判定します。人間でないと判断した場合は、エラーを出すなどして実行できないようにします。

v2 までは、画像などの認証でしたが、v3からは、ページにおけるユーザーの動きを見て、その判定を自動で行うので、ユーザーにとっては何も行う必要もなく負担をかけることもありません。

Ajax を使って reCAPTCHA 認証する

書いてみると意外と簡単ですが、これを行うには、reCAPTCHA 自体の仕様というか、全体の動きを把握してないとコードに落とし込みにくいと思います。

reCAPTCHAイメージ.png

  1. 認証が必要なページにGoogle指定のJavaScriptファイルを読み込む
  2. サイトキーを渡して、Googleのスクリプトでtokenを生成する
  3. token を submit などのタイミングでPOST送信する
  4. 自分のサイト上で受け取った token をGoogleのURLへ「シークレットキー」と一緒に投げる
  5. 認証可否が返ってくる(success: true, または、score: 0〜1など)
  6. その認証可否を参考にしてサーバ上で処理分けする

以上が、だいたいの流れになります。

これを理解した上で、順にコードに落とし込んでいきます。

「サイトキー」と「シークレットキー」を取得する

reCAPTCHA を使用するには、Googleアカウントが必要です。ログインした上で、それぞれのキーを取得するには、以下にアクセスします。

https://g.co/recaptcha/v3

key.png

これらのキーはいわゆる公開鍵のようなイメージでしょうか。その名のとおり「サイトキー」は、HTML 上で確認できるように記述します(ユーザーに見える必要はないですが、覗こうと思えば覗けるという意味です)。その一方で「シークレットキー」は、秘密鍵として自身のサーバ上に記述します。

認証が必要なページに Google 指定の JavaScript ファイルを読み込む

公式ドキュメント
https://developers.google.com/recaptcha/docs/v3

にもありますが、必要なページの「</body>」タグの手前付近で、Google指定のJavaScriptファイルを記述します。

<script src="https://www.google.com/recaptcha/api.js?render=サイトキー"></script>

サイトキーを渡して、Google のスクリプトで token を生成する

次に、あらかじめGoogleスクリプトによって、token を生成してどこかに保持しておく必要があります。

もし <form>タグを作っているようなら、

<input type="hidden" name="recaptcha_response" id="recaptchaResponse">

と、hidden 属性の input タグに埋め込んでしまっても良いでしょう。HTML ファイルが読み込まれると同時に、token を生成して、このタグに格納します。

$(document).ready(function() {
  grecaptcha.ready(function () {
    grecaptcha.execute('サイトキー', { action: 'contact' }).then(function (token) {
        var recaptchaResponse = $('#recaptchaResponse');
        recaptchaResponse.value = token;
    });
  });
});

ただ、今回は、Ajax を使って POST するので、グローバル変数に格納することで留めておきましょう。

// グローバル変数
var tokenData;

$(document).ready(function() {
  // Google reCAPTCHA v3 のトークンを生成
  grecaptcha.ready(function () {
    grecaptcha.execute('サイトキー', { action: 'submit' }).then(function (token) {
      tokenData = token;
    });
  });
});

なお、action に入力される文字列ですが、ここは書き分けることで、admin consoleから「リクエスト数」として集計・参照することができます。ですので、あまり集計や、後の参照を気にする必要がない場合は、文字列はなんであっても良いということです。

token を submit などのタイミングで POST 送信する

そして、上記で保存したグローバル変数を Ajax の POST 値に載せて送信します。

function sendData() {
  $.ajax({
    type: "POST",
    url: "register_submit.php",
    timeout: 10000,
    cache: false,
    data: {
      'mail': "m@hibara.org",
      'password': "password123",
      'recaptchaResponse': tokenData
    },
    dataType: 'text'
  })
  .done(function (data) {
    // "OK"
    location.href = "register_complete.php";
    return true;
  })
  .fail(function () {
    alert("サーバー内でエラーがあったか、サーバーから応答がありませんでした。");
    return false;
  })
  .always(function (data_or_jqXHR, textStatus, jqXHR_or_errorThrown) {
  });
}

自分のサイト上で受け取った token を Google の URL へ「シークレットキー」と一緒に投げる

投げた先、今回上記の例で言うと register_submit.php の中身はこう書きます。

if(isset($_POST['recaptchaResponse']) && !empty($_POST['recaptchaResponse'])) {
  $secret = 'シークレットキー';
  //get verify response data
  $verifyResponse = file_get_contents('https://www.google.com/recaptcha/api/siteverify?secret='.$secret.'&response='.$_POST['recaptchaResponse']);
  $responseData = json_decode($verifyResponse);

  if ( $responseData->success ) {
    if ( $responseData->score < 0.8 ) { // おまえ人間じゃねえ!
      // 認証スコアが低い
      exit();
    }
  }
  else {
    // 認証失敗
    exit();
  }
}
else {
  // POST値が正常に投げられてこなかった
  exit();
}

// ここまで抜けてきたとき、正常処理を行う

ここのスコア $responseData->score は、0〜1 の間を取ります。1に近いほど人間と判断できます。私が何回か試行してみた実感としては、ほぼ 0.9以上のスコアを叩くような気がします。この値については、サイトによって試行錯誤する必要があるかもしれません。

ここで得られる他の値、結果については、先に挙げた公式ドキュメントにあるとおりです。

{
  "success": true|false,      // whether this request was a valid reCAPTCHA token for your site
  "score": number             // the score for this request (0.0 - 1.0)
  "action": string            // the action name for this request (important to verify)
  "challenge_ts": timestamp,  // timestamp of the challenge load (ISO format yyyy-MM-dd'T'HH:mm:ssZZ)
  "hostname": string,         // the hostname of the site where the reCAPTCHA was solved
  "error-codes": [...]        // optional
}

JSONでの例になっていますが、この仕様に沿って、上記のPHPファイル内で処理すれば問題なく取得できるでしょう。

まとめ

とりあえず、Ajaxで reCAPTCHA v3 を実装した例が少なく、仕様全体のイメージもしにくかったので記事にしてみました。「最初にトークンを生成して投げる」ということを知れば、あとはいくらでも応用が利くとは思います。

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

PHPの基礎文法まとめ1

Rubyはある程度学んだところで、他言語として企業で使用頻度の高いPHPの学習をしております。
PHPの基礎文法で学んだことをメモのようにまとめていきます。

基本的なPHPコードの埋め込み
<?php
  // ここにPHPコードを記載
?>

上記のように書けば、タグの外にHTMLのコードも書ける。処理の途中でPHPコードに入ったり抜けたり出来る。
ちなみにPHPコードだけやPHPコードが最後の場合は、閉じタグ ?> は省く。
 

文字列の表示にはechoを使う。
<?php
echo 'こんにちは!' . PHP_EOL;
ターミナル出力
~ $ php main.php
こんにちは!
~ $
  • .(ピリオド)は文字列に繋げる役割。
  • PHP_EOVは文字列の改行を行う。(あくまでターミナル上で。ブラウザで改行処理される訳ではない点に注意)
  • ;(セミコロン)は命令の区切りにつける。これを忘れるとエラーが起こる。

 

変数宣言には$を頭に付ける
<?php
  $name = 'mizuno';

変数の命名規則は文字か _ (アンダースコア)が使える。数字や - (ハイフン)は使えない。

$name // OK
$_name // OK
$2020 // NG
$日本語も変数名にできる // OK
$first-name // NG

参照元:ドットインストール(PHP基礎文法編)

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