- 投稿日:2021-01-13T23:38:25+09:00
PHPでの「0」と「NULL」の比較について
いろんなところで言われてますがPHPでの
0
とNULL
の比較には注意が必要です。(ハマった)$hoge = 0; var_dump($hoge == NULL); // bool(true) var_dump($hoge === NULL); // bool(false) var_dump(is_null($hoge)); // bool(false)is_null()、もしくは型比較までやったほうが安全という学びでした。
- 投稿日:2021-01-13T23:30:27+09:00
Google reCAPTCHAをフォームに設置する
これを実装したくて、調べて、テストしてました。
忘れそうなので、備忘録も兼ねて記事にします。reCAPTCHAのAPIキーを取得する
1) reCAPTCHAにログイン
reCAPTCHAのサイトにアクセスして、Googleアカウントでログイン。
2) サイトの登録
項目 設定内容 ラベル 覚えやすい名前を入力してください reCAPTCHAタイプ 使いたいものを選択してください ドメイン 設置するサイトのドメインを入力してください オーナー メールアドレスが正しいか確認してください reCAPTCHAに同意する チェックをONにします アラートをオーナーに送信する ONにします 3) APIキーを取得する
APIキー画面が表示されたら、それぞれコピーしておきましょう。
また、シークレットキーは絶対に流出しないようにしましょう。管理画面>[設定]アイコン>[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=secretKey
のsecretKey
部分を、取得したシークレットキーに上書きしてください。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のままフォームを送信すると、通知ウィンドウが正しく表示されました。
また、チェックがONのときは正常に送信されることを確認しました。
- 投稿日:2021-01-13T22:44:11+09:00
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無事に起動できました。
まとめ
今回はGolangで作られたPHPのweb・アプリケーションサーバーを使った。サンプルを作ってみました。
おもしろいですが、不安定なのでプロダクション環境ではあまり使うのはおすすめできないですね。参考文献
- 投稿日:2021-01-13T22:44:11+09:00
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無事に起動できました。
まとめ
今回はGolangで作られたPHPのweb・アプリケーションサーバーを使った。サンプルを作ってみました。
おもしろいですが、不安定なのでプロダクション環境ではあまり使うのはおすすめできないですね。参考文献
- 投稿日:2021-01-13T22:44:11+09:00
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無事に起動できました。
まとめ
今回はGolangで作られたPHPのweb・アプリケーションサーバーを使った。サンプルを作ってみました。
おもしろいですが、不安定なのでプロダクション環境ではあまり使うのはおすすめできないですね。
- 投稿日:2021-01-13T22:00:46+09:00
Laravelで手軽にログイン認証機能を実装する
【前提条件】
Lamp環境が正常にインストール出来ている。
Node.jsのインストールが出来ている。
データ・ベースの設定が出来ている。
artisanコマンドがある程度扱える。Node.jsがインストール出来ていない方は@seiba様の記事を参照して下さい
【引用リンク】
https://qiita.com/seibe/items/36cef7df85fe2cefa3ea1、バージョンを確認する
まずはLaravelのバージョンを確認しましょう、以下のコマンドを入力して下さい。
php artisan --versionLaravelのバージョンが6系か7系かを確認します。
2、Laravel/uiをインストールする。
6系の場合は以下のコマンドでインストール
composer require laravel/ui:^1.0 --dev7系の場合は以下のコマンドでインストール
composer require laravel/ui:^2.43、認証機能を実装する
6系、7系共通で以下のコマンドを入力してログイン機能を実装する
php artisan ui vue --authこの時点ではログイン機能は完成していません、この時点でREGISTER場面を
見てみると………フロントエンド部分が実装されていません。
4、フロントエンド部分を実装する〜完成
npmをインストールする
npm installnpmをビルドする
npm run dev最期に忘れずにmigrationコマンドででログイン情報を格納するテーブルを作成する。
php artisan migration完成です
Laravelを立ち上げてみて右端のRegisterをクリックしてみる
すると先程は実装されていなかったフロントエンド部分の実装がなされている。
実際に登録してみる
番外編:どのようにデータが格納されているか
実際にパスワードが格納されているテーブルを見てみるとパスワードが暗号化されて格納されており、手軽に実装出来てかなり高度な認証機能である事がわかる。
| 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
- 投稿日:2021-01-13T21:12:11+09:00
"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 hello5. PHP
HTML/CSSと一緒に使われたりしますね。
<?php echo "Hello world\n"; ?>終わりに
以上です。いかがでしたでしょうか?
Ruby
はやっぱり短かったですね。それが長所ですよね。PHP
のechoてエコーって読むんですね。初めて知りました。若輩者ゆえ、もしかしたら間違いがあるかもしれないので、その時はコメントなんかで教えてください。
- 投稿日:2021-01-13T20:45:12+09:00
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.ymlversion: "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こだわったポイント
Dockerfileのビルドが必要ないこと。
もはやカップ麺を作るより速く、お湯を沸かす時間だけで作れてしまうほどシンプルな構成です。作った理由
Dockerfileのビルドの時間がかかるから。
Laravelのお試し環境を作るのに最強の構成を作るまででもないなという時。GitHub
オススメの記事
- 投稿日:2021-01-13T20:23:31+09:00
【Laravel】コントローラーとは?作成や編集方法を実例で解説。
Laravelのコントローラーについて。実際にコントローラーを作成してブラウザに表示させることで理解を深める。
目次
コントローラーとは?
Laravelのコントローラーでは、クラスを作成しメソッドを定義することができる。
ルーティングファイル(web.php)などにメソッドを記述していくとファイル長が膨大になってしまうため、コントローラーを使用することで、メソッドを利用しやすくなる。
コントローラーの作成方法
・
$ php artisan make:controller コントローラー名
コントローラー名は冒頭大文字のキャメル表記。
例$ php artisan make:controller HelloController Controller created successfully.
コントローラーの保存場所
artisanコマンドを実行すると、app/Http/Controllers内に新たにファイルが生成される。
↓
専用のディレクトリの中に入れたい場合
コントローラー名の前にディレクトリ名を付ければOK。
例:
php artisan make:controller Test/HelloController
コントローラーの中身
<?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
・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.phpRoute::get('msg', 'App\Http\Controllers\HelloController@message');URIがmsgにアクセスしたら、HelloControllerのmessageメソッドを実行する。
以上で完了。
▼ブラウザの表示
コントローラー指定時の注意点
Laravel7までは、
App\Http\Controllers\
が省略できたが、Laravel8からは省略するとエラーが表示されるようになった。対策として完全な名前空間で指定した。
他にも、Laravel7の環境と同じにする方法や、アクション構文を使用する方法がある。
Laravel7と同じ環境にする
以下ファイルのコメントアウトを解除する。
app > Providers > RouteServiceProvider.php
解除するコメントアウト//protected $namespace = 'App\\Http\\Controllers';
RouteServiceProvider.phpprotected $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.phpRoute::get('msg', 'HelloController@message');
アクション構文を使用する
ルーティングファイルの冒頭で、
use
を使ってHelloControllerクラスをインポートする。・
use App\Http\Controllers\HelloController;
getメソッドの第二引数で以下を指定。
・[コントローラー名::class, 'メソッド名']
web.phpuse 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を作成する。
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.phpRoute::get('xxx', 'HelloController@message');以上で完了。
>section, yield, extendの詳細はこちらを参照
▼ブラウザの表示
ページタイトルと、ページの内容に、コントローラーで定義した変数が表示されている。
- 投稿日:2021-01-13T19:02:36+09:00
【Laravel】Illuminateとは何か?ファイルはどこにあるのか?
Laravelでコントローラーを作成した場合などに、
use Illuminate\Http\Request;
といった記述がある。これは名前空間の
Illuminate\Http\Request
をインポートしているということだが、そもそもIlluminate
とはどこにあり、何なのか?Illuminateはどこにあるのか?
Illuminateは、laravelでプロジェクトを作成した時に自動生成される、vendorディレクトリ配下に存在する。
vender > laravel > framework > src > Illuminate
vendorディレクトリはcomposerと依存関係にある。
Illuminateとは?
IlluminateはLaravelのコンポーネントが入っている。
Illuminate\Http\Request
クラスの、hasメソッドのように、各クラスとメソッドが定義されている。Laravelでデフォルトで使えるありがたいメソッドがたくさんあるのは理解できたが、その名前とマッチしなさすぎる、、
IlluminateはLaravel4のコードネームだそうで、このタイミングでcomposerを通じてframeworkをインストールするように認め、それを記念して(?)Illuminateというフォルダに格納されているらしい。(歴史を知らない人にはとにかくわかりにくい!!)
まぁ、Laravelの大事なコンポーネントが置いてある場所ということ。
・参考
Illuminateメソッド一覧
- 投稿日:2021-01-13T17:37:10+09:00
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
- 投稿日:2021-01-13T17:05:28+09:00
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; ?>
- 投稿日:2021-01-13T16:25:07+09:00
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/後、数えきれないぐらい関連記事を参考にしました笑
そして、他のやり方もあります!課題は入力値が残らなくなってしまったのでどこかのタイミングで直します!
何かご指摘いただけたらありがたいです!
- 投稿日:2021-01-13T15:17:49+09:00
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に登録すれば良い。便利。
- 投稿日:2021-01-13T13:34:52+09:00
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" ] ] ); }
- 投稿日:2021-01-13T13:30:28+09:00
[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句は使わないようにすべし!
参考
- 投稿日:2021-01-13T11:21:16+09:00
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標準メソッドになります。フレームワークを利用している場合は、フレームワークごとに実装は異なりますので、フレームワークのドキュメントより関連する内容を確認して対応してください。
参考
- 投稿日:2021-01-13T11:05:00+09:00
PHP + Ajax(jQuery) + reCAPTCHA v3 を実装する
reCAPTCHA v3 とは、クローラーやロボットによるアクセスを制限して、無駄にサイトアクセスさせないために設置する認証装置です。
主に、連投されては困るようなコンタクトフォームや、コメント投稿、ユーザー登録などの画面に設置して、そのボタンを押すのが人間なのかどうかを判定します。人間でないと判断した場合は、エラーを出すなどして実行できないようにします。
v2 までは、画像などの認証でしたが、v3からは、ページにおけるユーザーの動きを見て、その判定を自動で行うので、ユーザーにとっては何も行う必要もなく負担をかけることもありません。
Ajax を使って reCAPTCHA 認証する
書いてみると意外と簡単ですが、これを行うには、reCAPTCHA 自体の仕様というか、全体の動きを把握してないとコードに落とし込みにくいと思います。
- 認証が必要なページにGoogle指定のJavaScriptファイルを読み込む
- サイトキーを渡して、Googleのスクリプトでtokenを生成する
- token を submit などのタイミングでPOST送信する
- 自分のサイト上で受け取った token をGoogleのURLへ「シークレットキー」と一緒に投げる
- 認証可否が返ってくる(success: true, または、score: 0〜1など)
- その認証可否を参考にしてサーバ上で処理分けする
以上が、だいたいの流れになります。
これを理解した上で、順にコードに落とし込んでいきます。
「サイトキー」と「シークレットキー」を取得する
reCAPTCHA を使用するには、Googleアカウントが必要です。ログインした上で、それぞれのキーを取得するには、以下にアクセスします。
これらのキーはいわゆる公開鍵のようなイメージでしょうか。その名のとおり「サイトキー」は、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 を実装した例が少なく、仕様全体のイメージもしにくかったので記事にしてみました。「最初にトークンを生成して投げる」ということを知れば、あとはいくらでも応用が利くとは思います。
- 投稿日:2021-01-13T10:19:18+09:00
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