- 投稿日:2021-02-25T22:00:54+09:00
【PHP備忘録】ログインの流れ
ログインの大まかな流れ。
前提
・join/index.php(登録画面)
・join/checked.php(登録確認画面)
・login.php(ログイン画面)
・index.php(ログインされた画面)formのactionは空にしておく
あと各フォルダの最初にこれを忘れない
session_start(); require('../dbconnect.php');これで、つながる
$_SESSION['join'] = $_POST;後これがPOSTにある値をセッションにさせたやつ
HTMLにて変数で代入する
例 この場合だと打たれた名前が表示されるようになる
check.php<dd> <?php print (htmlspecialchars($_SESSION['join']['name'],ENT_QUOTES));?> </dd>これを各それぞれの値にやる
あとこれめちゃくちゃやる、もうほぼこれ。DB接続->登録情報をINSERT
ここでドキドキのDB接続
check.phpif(!empty($_POST)) { $statement= $db->prepare('INSERT INTO members SET name=?, email=?, password=?, picture=?, created=NOW()'); $statement -> execute(array( $_SESSION['join']['name'], $_SESSION['join']['email'], // 「sha1」というのは記号化してくれるもの(不可逆) sha1($_SESSION['join']['password']), $_SESSION['join']['image'], ));終わったらデータが残らないように削除して完了。
check.phpunset ($_SESSION['join']); header('Location: thanks.php'); exit();これでDBにINSERTされた。
ログイン画面で呼び出し
さて…
login.phpif($_POST['email'] !='' && $_POST['password'] !== '' ) { $login = $db ->prepare('SELECT * FROM members WHERE email=? AND password=?'); $login ->execute(array( $_POST['email'], sha1 ($_POST['password']) )); $member = $login->fetch(); if($member) { $_SESSION['id'] = $member['id']; $_SESSION['time'] = time(); header('Location: index.php'); exit();最初の段落から
POSTのEメール・パスワードがともに埋まってる時
→DB接続(memberのテーブルでEメール・パスワードの値が?をさがして!)
→POSTにあるこれさがして!
↓
取得して$memberにわかりやすくして!
↓
SESSIONとmemberのID一緒だったらindex.phpにいっていいよ!
って感じ?
違うIDの人が入らないように
index.phpif (isset($_SESSION['id']) && $_SESSION['time'] +3600 > time() ) { $_SESSION['time'] = time(); $members = $db->prepare('SELECT * FROM members WHERE id=?'); $members ->execute(array($_SESSION['id'])); $member= $members -> fetch(); } else { header('Location: login.php'); exit();}ん~、多分IDとパスワードの値が入ってたらそのログインした時間を現在の時間に上書きする。
そして、ログインしたユーザーの情報を持ってくることができた。一通りは以上!
ps.用語意識して覚えてないから説明するの抽象的になってしまうなぁ
- 投稿日:2021-02-25T21:39:51+09:00
多次元配列を時間順に並び替える
概要
データベースの結果を多次元の配列に代入して時間順に並び替えたのでその方法をメモします。
結論
array_multisort( array_map( "strtotime", array_column( $data, "created_at" ) ), SORT_DESC, $array ) ;一つずつ見ていきます。
実装
こんな感じの配列
$data=[ { "post_id": 76, "user_id": 3, "description": "hogehoge", "created_at": "2021-02-08T09:59:52.000000Z", }, { "post_id": 77, "user_id": 2, "description": "hogehoge", "created_at": "2021-02-11T12:08:44.000000Z", } { "post_id": 78, "user_id": 2, "description": "hogehoge", "created_at": "2021-01-26T21:50:47.000000Z", } ];時間順に並べたいので
created_at
から参照して並び替えます。array_multisort( ソートしたい配列 , ソート順 ,ソート方法(省略可) , 追加の配列 )
今回ソートしたい配列は$dataですが
created_at
が英文日付なので、UNIXタイムスタンプ形式にする必要があります。
ですのでarray_map()関数を使います。array_map(コールバック関数, $配列)
ですので、
array_map("strtotime",array_column( $data, "created_at" ) )で日付を比較できるように変換します。
array_multisort( array_map( "strtotime", array_column( $data, "created_at" ) ), SORT_DESC, $array ) ;結果
$data=[ { "post_id": 78, "user_id": 2, "description": "hogehoge", "created_at": "2021-01-26T21:50:47.000000Z", }, { "post_id": 76, "user_id": 3, "description": "hogehoge", "created_at": "2021-02-08T09:59:52.000000Z", }, { "post_id": 77, "user_id": 2, "description": "hogehoge", "created_at": "2021-02-11T12:08:44.000000Z", } ];無事時間順に並び替えることができました。
- 投稿日:2021-02-25T20:40:24+09:00
Laravel 8.x Seeder生成で怒られる
Seederでダミーデータを作成する際、グローバルヘルパや関数を使用する際の落とし穴があります。
php artisan make:seed UsersTableSeeder
で作成すると以下のSeederが生成されます。UsersTableSeeder.php<?php namespace Database\Seeders; use Illuminate\Database\Seeder; class UsersTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { // } }ダミーデータを
run
メソッド内に記述しSeederを実行するとヘルパや関数を使用している場合は怒られます。UsersTableSeeder.php<?php namespace Database\Seeders; use Illuminate\Database\Seeder; class UsersTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { DB::table('users')->insert([ 'name' => 'hoge', 'password' => Hash::make('hoge'), 'remember_token' => Str::random(30), ]); } }対処法
ファザードやヘルパを使用する際は宣言してあげる必要があるらしいです。
UsersTableSeeder.php<?php namespace Database\Seeders; use Illuminate\Database\Seeder; //最初からある use Illuminate\Support\Facades\DB; //DBファザード使用の為追加 use Illuminate\Support\Facades\Hash; //Hashファザード使用の為追加 use Illuminate\Support\Str; //strメソッド使用の為追加 class UsersTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { DB::table('users')->insert([ 'name' => 'hoge', 'password' => Hash::make('hoge'), 'remember_token' => Str::random(30), ]); } }参考
- 投稿日:2021-02-25T20:24:41+09:00
VSCode で環境を汚さず好きなバージョンのPHP開発環境を作る手順
必要なもの
PC(Mac or Window or Linux)
Docker for Desktop(Toolkitではない方)
VSCode(Remote Development拡張機能が必要)
インターネット環境
ブラウザ(Chrome等)VSCodeを起動
作業フォルダを開く
左上に縦に並んでいる中の Explorer ボタン
を押します。Open Folderボタンを押し、作業するフォルダを選択します。ここでは空のphpフォルダを選択しました。
Dockerfile 作成
ファイル一覧の PHP(選択したフォルダ) の右側にあるファイル作成ボタン
を押します。ファイル名部分に Dockerfile と入力します。(Dは大文字)
ファイルの内容は
FROM php:5.4-apache
と入力します。(5.4はPHPのバージョンで好きなバージョンを指定できます。)
CTRL+Sキーを押してファイルを保存します。
Docker開発環境を作成
※緑部分がない場合は、Remote Development拡張機能が入っていないのでインストールしてください。
上部中央にリストが表示されるので Remote-Containers: Reopen in Container を選択します。
続けてリストが表示されるので、 From 'Dockerfile' を選択します。これで作成したDockerfileからDockerコンテナが起動します。
すると、.devcontainer フォルダ、 devcontainer.json が生成されます。このファイルでDocker環境を起動するときの設定ができます。
※これらのフォルダ、ファイルリストは Docker内のものが表示されています。
PHP[DEV CONTAINER:EXISTING DOCKERFILE] がそれを表しています。
ローカルのphpフォルダと内容が同期されるように設定されています。devcontainer.json 設定変更
/var/www/html の内容が同期されるよう設定します。
以下の2行を一番外の{}内に追加します。
"workspaceFolder": "/var/www/html", "workspaceMount": "type=bind,source=${localWorkspaceFolder},target=/var/www/html",修正前// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: // https://github.com/microsoft/vscode-dev-containers/tree/v0.158.0/containers/docker-existing-dockerfile { "name": "Existing Dockerfile", // Sets the run context to one level up instead of the .devcontainer folder. "context": "..", // Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename. "dockerFile": "../Dockerfile", // Set *default* container specific settings.json values on container create. "settings": { "terminal.integrated.shell.linux": null }, // Add the IDs of extensions you want installed when the container is created. "extensions": [] // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], // Uncomment the next line to run commands after the container is created - for example installing curl. // "postCreateCommand": "apt-get update && apt-get install -y curl", // Uncomment when using a ptrace-based debugger like C++, Go, and Rust // "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ], // Uncomment to use the Docker CLI from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker. // "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ], // Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root. // "remoteUser": "vscode" }修正後// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: // https://github.com/microsoft/vscode-dev-containers/tree/v0.158.0/containers/docker-existing-dockerfile { "name": "Existing Dockerfile", // Sets the run context to one level up instead of the .devcontainer folder. "context": "..", // Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename. "dockerFile": "../Dockerfile", // Set *default* container specific settings.json values on container create. "settings": { "terminal.integrated.shell.linux": null }, "workspaceFolder": "/var/www/html", "workspaceMount": "type=bind,source=${localWorkspaceFolder},target=/var/www/html", // Add the IDs of extensions you want installed when the container is created. "extensions": [] // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], // Uncomment the next line to run commands after the container is created - for example installing curl. // "postCreateCommand": "apt-get update && apt-get install -y curl", // Uncomment when using a ptrace-based debugger like C++, Go, and Rust // "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ], // Uncomment to use the Docker CLI from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker. // "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ], // Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root. // "remoteUser": "vscode" }左下以下の部分をクリックし、Remote-Containers: Rebuild Container を選択します。
※Dockerfile や devcontainer.json を変更した時はこれで設定を反映させます。ターミナルを確認すると /var/www/html が初期ディレクトリになり、
ここにファイルを作ればサーバで公開されブラウザで確認できるようになります。
phpファイル配置
ファイル一覧の HTML[DEV... の右側にあるファイル作成ボタン
を押します。
作成された枠の中にファイル名として index.php を入力します。
ファイルの内容に
<?php phpinfo();
と入力します。
CTRL+Sでファイルを保存します。
動作確認
TERMINALを開いて、
apachectl start
と入力し、Enter を押します。
左上に縦に並んでいるアイコンの中の Remote Expoloer ボタン
を押します。
PORTS部分に 80 →... の行があるので、右の Open in Browser ボタン
を押します。※56598の番号は環境によって変わります。
- 投稿日:2021-02-25T17:05:05+09:00
PHPでMySQLの接続確認をする
MySQLのクライアントが入っていないサーバーでMySQLアカウントの確認をする方法です。
ssh 対象のサーバ vi test.php ------------------------------------------------------------------------------------------------- <?php define('HOSTNAME', 'hoge01'); define('DATABASE', 'db1'); define('USERNAME', 'test'); define('PASSWORD', 'hogehoge'); try { $db = new PDO('mysql:host=' . HOSTNAME . ';dbname=' . DATABASE, USERNAME, PASSWORD); echo "OK\n"; } catch (PDOException $e) { echo "NG\n"; echo $e->getMessage()."\n"; } ------------------------------------------------------------------------------------------------- php test.php
- 投稿日:2021-02-25T16:21:41+09:00
Laravel DBレコードの存在判定
LaravelのクエリビルダでDBレコードの存在チェック
記事内容
Laravelでオリジナルアプリのバックエンドを実装していく際に、DBにレコードが存在すればtrueを返し(ページ遷移)、レコードが存在しなければfalseを返す(エラーメッセージを表示する)という機能を実装したので、備忘録として残したいと思います。
開発環境
Laravel 8.22.1
PHP:8.0.1
MySQL:8.0.23
MacOS:11.1 ( Big Sur )
DBレコードの存在チェック
○○Controller.phpDB::table('table名')->where(column, $data)->exists();クエリビルダの基本的な書き方に、existsメソッドを繋げて記述することで簡単にレコードの存在を判定できます。
実際の記述
今回僕は、下記のように記述しました。
内容としては、ユーザーによるフォームからのリクエスト内容と一致するレコードが、存在するかどうかで条件分岐しています。
○○Controller.php$user = User::all()->where('id', $request->id)->first(); $msg = ['msg' => '入力されたIDは存在しません']; if (DB::table('users')->where('id', $request->id)->exists() && [$request->id === $users->id]) { return redirect()->route('home'); } else { return view('login', $msg);ifのカッコ内の記述により、DB内にレコードが存在していて且つ、フォームから送信された内容と一致する場合は、homeビューが返され、DBにレコードが存在しない場合は$msg変数の内容とともにloginビューが返される。
あとがき
今回のアプリを作っていく過程で、フォームからDBにレコードが存在しない値を入力された際、
「Attempt to read property "id" on null」というエラーにつまづき、なかなか前へ進めなかったので、その解決策を模索していたところ、本記事に記載した対処法に至りました。他にも解決策はあると思いますので、laravelの公式ドキュメントや他の記事を参考にして、引き続きアプリの実装をしていきたいと思います。
- 投稿日:2021-02-25T14:52:41+09:00
PHPでAWS S3からディレクトリ単位でダウンロード
前提条件
AWS SDK for PHP 3.x を利用
やってみた感想
CommandPool利用のために配列を作成する必要がなく
コードもシンプルになるのでよいAWS S3 バケットを再帰的にダウンロード
同期転送
sample.php<?php use Aws\S3\S3Client; use Aws\S3\Transfer; $client = new S3Client([ 'region' => '****', 'version' => 'latest', ]); // from $source = 's3://bucket/foo'; // to ローカルディレクトリのパス $dest = '/path/to/destination/dir'; $manager = new Transfer( $client, $source, $dest, ); $manager->transfer();非同期転送
sample.php<?php use Aws\S3\S3Client; use Aws\S3\Transfer; $client = new S3Client([ 'region' => '****', 'version' => 'latest', ]); // from $source = 's3://bucket/foo'; // to ローカルディレクトリのパス $dest = '/path/to/destination/dir'; $manager = new Transfer( $client, $source, $dest, ); $promise = $manager->promise(); $promise ->then(function () { echo 'Done!'; }) ->otherwise(function ($reason) { echo 'Transfer failed'; });参考
- 投稿日:2021-02-25T13:49:11+09:00
LaravelでStrategyパターン
Strategyパターンやってみました。
割と使い道があるんじゃないかと思います。バージョン
Laravel 6.18.40
PHP 7.4.4Strategyパターン
Strategyパターンはstrategy(戦略)という言葉の意味からわかるように、状況に応じて処理を動的に切り替えることを目的としたデザインパターンです。
それぞれの処理をクラスとして定義し、共通の呼び出し部分から呼び出して処理を代替できるようにします。(オブジェクト指向の文脈ではdelegationと言われるようです)実装
仕様
例えば、ユーザーが単発の仕事に応募してその報酬をもらえるようなサイトがあるとします。仕事情報は管理画面からCSVでアップロードする仕様になっています。アップされたCSVの内容はDBに保存されます。
仕事にはいくつかの種類があり、扱うデータが異なります。なので管理者は様々なフォーマットのCSV(ヘッダー行が1行だったり2行だったり、列数が違っていたりなど)をアップロードします。管理画面にあるCSVアップロードフォームは1つで、フォーム内にはファイル用inputとファイルのタイプを選択するselectが存在します。
<!--こんな感じ--> <form action="{{route('route_name')}}" method="post"> <input type="file" name="uploaded_file" /> <select name="file_type"> @foreach(config('values.file_type') as $label => $fileType) <option value="{{$fileType}}">{{$label}}</option> @endforeach </select> </form>考えること
フォームが送信されたらコントローラー内のメソッドでリクエスト値を受け取ります。それからCSVファイル内のデータを読み取って、そのデータをデータベースに保存します。
仕様にもある通り、アップロードされるCSVのフォーマットは何種類もあります。1つのメソッドにまとめてifなどで条件分岐しながらファイルを処理することは可能ですが、読みにくくて変更に弱い処理になってしまいそうです。
Strategyパターンを使えば、今回の場合だと、アップロードされるファイルのタイプごとにクラスを分けてその中で処理を行うことができます。Dto
先にデータをやりとりするためのDtoクラスを定義しておきます。
use Illuminate\Http\UploadedFile; class FileImportDto { public UploadedFile $uploadedFile; public int $fileType; public string $fileName; public string $filePath; public function __construct(UploadedFile $uploadedFile, int $fileType, string $fileName, string $filePath) { $this->uploadedFile = $uploadedFile; $this->$fileType = $fileType; $this->$fileName = $fileName; $this->$filePath = $filePath; } }StrategyInterface
CSVファイルのタイプごとに処理が違うので、最終的にタイプごとにクラスを分ける前提です(上の図の各ConcreteStrategyに相当)。そのクラスの雛形となるInterfaceを作成します。
全てのクラスで必要な処理は
- CSVファイルの中身の読み取り
- データをDBに保存
この2つです。
use App\Models\FileImportDto; interface StrategyInterface { /** * @param FileImportDto $fileDto * @return array */ public function readData(FileImportDto $fileDto): array; /** * @param array $fileData * @return mixed */ public function storeData(array $fileData); }具体的な処理
StrategyInterfaceの実装クラスを作成します。何個でも作成できるのがStrategyパターンのメリットです。今回は一旦3種類にします。
上の仕様の例で仕事情報に種類があると書きましたが、"オフィスワーク"、"飲食"、"運送"の3種類あると仮定します。
具体的な処理内容は今回関係ないので省略します。use App\Models\FileImportDto; class OfficeWorkFile implements StrategyInterface { public function readData(FileImportDto $fileDto): array { //CSVファイルの中身を読み取って配列に詰めて返す処理 (略) } public function storeData(array $fileData) { //配列を受け取って、中身をDBに保存する処理 (略) } }use App\Models\FileImportDto; class RestaurantFile implements StrategyInterface { public function readData(FileImportDto $fileDto): array { //CSVファイルの中身を読み取って配列に詰めて返す処理 (略) } public function storeData(array $fileData) { //配列を受け取って、中身をDBに保存する処理 (略) } }use App\Models\FileImportDto; class DeliveryFile implements StrategyInterface { public function readData(FileImportDto $fileDto): array { //CSVファイルの中身を読み取って配列に詰めて返す処理 (略) } public function storeData(array $fileData) { //配列を受け取って、中身をDBに保存する処理 (略) } }StrategyFactory
具体的な処理を行うクラスを実装できました。次に必要なのは、ファイルのタイプによってどのクラスをインスタンス化するか判別する処理です。
ここについては何通りか方法があると思いますが、今回はStrategyFactoryというクラスを作成してそこで行うことにします。class StrategyFactory { /** * @param int $fileType * @return StrategyInterface */ public static function createStrategy(int $fileType): StrategyInterface { $strategy = null; switch ($fileType) { case config('values.file_type.office_work.value'): $strategy = new OfficeWorkFile(); break; case config('values.file_type.restaurant.value'): $strategy = new RestaurantFile(); break; case config('values.file_type.delivery.value'): $strategy = new DeliveryFile(); break; } return $strategy; } }Context
具体的な処理を、上の図でいう各ConcreteStrategyにdelegationするクラスです。今回の例では場合に応じてOfficeWorkFileクラス、RestaurantFileクラス、もしくはDeliveryFileに処理を行ってもらうクラスということになります。
StrategyFactory::createStrategy()でどのクラスをインスタンス化するか判別し、executeで処理を実行します。class Context { private StrategyInterface $strategy; public function __construct(FileImportDto $fileDto) { $this->setStrategy($fileDto->fileType); } /** * @param int $fileType */ public function setStrategy(int $fileType) { $this->strategy = StrategyFactory::createStrategy($fileType); } /** * @param FileImportDto $fileDto */ public function execute(FileImportDto $fileDto) { $fileData = $this->strategy->readData($fileDto); $this->strategy->storeData($fileData); } }Contextクラスを動かすメソッド
Contextクラスの処理を動かすメソッドが必要です。これはとてもシンプルで、以下のようなものになります。
public function processFile(FileImportDto $fileDto) { $context = new Context($fileDto); $context->execute($fileDto); }こちらの処理をどこに置くかは好みが分かれると思います。これぐらいならControllerに置いてもよさそうな気がしますが、ビジネスロジック用にServiceクラスというものを作成してそちらに配置することにします。
use App\Models\FileImportDto; use App\Services\Strategies\Context; class FileImportService { public function processFile(FileImportDto $fileDto) { $context = new Context($fileDto); $context->execute($fileDto); } }FormRequest
あとはControllerからServiceクラスにあるprocessFileを呼び出せば完了です。
しかしその前に、リクエスト値をDtoに変換しなければなりません。これをそのままController内で行うのは少しfatな感じになりそうな気がします。
せっかくLaravelを使っているので、FormRequest内に変換処理を書くことにします。class FileImportRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'uploaded_file' => /*バリデーションルール*/, 'file_type' => /*バリデーションルール*/ ]; } /** * convert request values into FileImportDto * * @return FileImportDto */ public function convertIntoDto(): FileImportDto { $uploadedFile = $this->file('uploaded_file'); return new FileImportDto( $uploadedFile, (int)$this->input('file_type'), $uploadedFile->getClientOriginalName(), $uploadedFile->path() ); } }Controller
Serviceクラスにある処理を呼び出すのみです。
Dtoへの変換処理はFormRequestに定義してあるので、かなりスッキリしたものになりました。class FileImportController extends Controller { private $service; public function __construct(FileImportService $service) { $this->service = $service; } public function importFile(FileImportRequest $request) { $this->service->processFile($request->convertIntoDto()); return redirect(route('route_name')); } }これでStrategyパターンは完成です?
- 投稿日:2021-02-25T12:04:54+09:00
フォームデータの送信
form要素
データを送信する方法を定義します。その属性すべてが、ユーザーが送信ボタンを押すと送信されるリクエストを調整できるように設計されています。
action 属性
action 属性は、どこにデータを送信するかを定義します。値は妥当な相対/絶対 URL でなければなりません。この属性が与えられなかった場合は、フォームが含まれているページの URL にデータが送信されます。
GET メソッド
GET メソッドは、サーバーに対して指定したリソースを返すよう求めるためにブラウザーが使用するメソッドです。
POST メソッド
POST メソッドは少し異なります。これは、HTTP リクエストの本文で提供したデータを考慮したレスポンスの要求を、ブラウザーがサーバーに送信するためのメソッドです。
- 投稿日:2021-02-25T09:54:30+09:00
計算と三項演算子でじゃんけん
[PHP]じゃんけんプログラムと、if文なしでじゃんけんを読んで自分でもやってみた。
まずは、じゃんけんの勝敗を表にする。
9パターンしかないので、そこに数値を入れると下のようになります。
グー(0) パー(1) チョキ(2) グー(0) あいこ(0) 負け(2) 勝ち(1) パー(1) 勝ち(1) あいこ(0) 負け(2) チョキ(2) 負け(2) 勝ち(1) あいこ(0) 縦軸を
$player_hand
、横軸を$pc_hand
とすると、計算方法は下の通りです。$result = ($player_hand - $pc_hand + 3) % 3;これを邪悪な三項演算子に放り込む。
$judgment = $result == 0 ? "あいこ" : ($result == 1 ? "勝ち" : "負け");特に目新しい物がないけど、こんな感じになる。
<?php const RPS = ["グー", "パー", "チョキ"]; foreach (RPS as $player_hand) { foreach (RPS as $pc_hand) { $judgment = janken($player_hand, $pc_hand); echo $player_hand, ":", $pc_hand, ":", $judgment, PHP_EOL; } } function janken(string $player_hand, string $pc_hand): string { $result = (array_search($player_hand, RPS) - array_search($pc_hand, RPS) + 3) % 3; $judgment = $result == 0 ? "あいこ" : ($result == 1 ? "勝ち" : "負け"); return $judgment; }普通すぎる結果になりました。
- 投稿日:2021-02-25T09:54:30+09:00
計算でじゃんけん
[PHP]じゃんけんプログラムと、if文なしでじゃんけんを読んで自分でもやってみた。
まずは、じゃんけんの勝敗を表にする。
9パターンしかないので、そこに数値を入れると下のようになります。
グー(0) パー(1) チョキ(2) グー(0) あいこ(0) 負け(2) 勝ち(1) パー(1) 勝ち(1) あいこ(0) 負け(2) チョキ(2) 負け(2) 勝ち(1) あいこ(0) 縦軸を
$player_hand
、横軸を$pc_hand
とすると、計算方法は下の通りです。$result = ($player_hand - $pc_hand + 3) % 3;
これを邪悪な三項演算子に放り込む。
配列だけ済むので修正しました。$judgment = $result == 0 ? "あいこ" : ($result == 1 ? "勝ち" : "負け");特に目新しい物がないけど、こんな感じになる。
<?php const RPS = ["グー", "パー", "チョキ"]; const JUDGMENT = ["あいこ", "勝ち", "負け"]; foreach (RPS as $player_hand) { foreach (RPS as $pc_hand) { $judgment = janken($player_hand, $pc_hand); echo $player_hand, ":", $pc_hand, ":", $judgment, PHP_EOL; } } function janken(string $player_hand, string $pc_hand): string { $result = (array_search($player_hand, RPS) - array_search($pc_hand, RPS) + 3) % 3; return JUDGMENT[$result]; }普通すぎる結果になりました。
- 投稿日:2021-02-25T07:25:00+09:00
【備忘録】写真のアップロード方法
最近、エラーが埋まってる埋まってないで考えると楽になるのかなと感じる
HTMLにて
これをすることにより、ファイルから選ぶことができる
join.phpinput type="file"PHPにて
理解はしていない…
上のPHPで名前(?)を決めるjoin.php$fileName = $_FILES['image']['name']; if(!empty($fileName)) { $ext = substr($fileName, -3); //写真のタイプを選定し、エラーを作る// if($ext != 'jpg' && $ext !='gif') { $error['image'] = 'type'; } }PHPにて
上の2行でフォルダ内にアップロードできるようになる
join.phpif(empty($error) ) { //写真のアップロードの決まり文句らしい// $image = date('YmdHis') . $_FILES['image']['name']; move_uploaded_file($_FILES['image']['tmp_name'], '../member_picture/' . $image); //ちょっとしたまとめ// $_SESSION['join'] = $_POST; $_SESSION['join']['image'] = $image; header('Location: check.php'); exit(); }次の画面にて
これで表示OK
check.php<?php if($_SESSION['join']['image'] !==''):?> <img src="../member_picture/<?php print (htmlspecialchars($_SESSION['join']['image'],ENT_QUOTES));?>" > <?php endif ;?>
- 投稿日:2021-02-25T07:19:25+09:00
php require とrequire_oneceの違い , includeとの違い, パスの指定方法についてなど
includeとrequireの違いやパス指定のまとめ
違いについて
再読み込みを同じファイル内で行われた際の挙動が違う
参考:https://qiita.com/awesam86/items/3fa28e23c95ca74caddc
試してみた(index.phpでr.php, rone.phpを読み込み検証)
r.php
<?php $r = 'hoge'rone.php
<?php $rone = 'hoge';index.php
<?php require 'r.php'; require_once 'rone.php'; echo $r. "\n"; //hoge echo $rone. "\n"; //hoge $r = 'hoge_change'; $rone = 'hoge_change'; echo $r. "\n"; //hoge_change echo $rone. "\n"; //hoge_change require 'r.php'; require_once 'rone.php'; echo $r. "\n"; //hoge(requireではファイルの読み込みが再度行われるため値も$r = 'hoge'が再読み込みされている) echo $rone. "\n"; //hoge_change(require_onceでは一度しか読み込まれないため表示がhoge_changeのまま)includeとの違いは
エラー出た時requireは画面全体が消える。includeは警告の為消えずに他の画面が表示される。
参考:公式ドキュメント
include
パスの指定について
- 投稿日:2021-02-25T01:30:56+09:00
if文なしでじゃんけん
[PHP]じゃんけんプログラム - Qiitaを読んで、自分ならどうするか考えてみた。
解答
複雑に考えればいくらでも複雑にできるけれども、とりあえずこのくらいにした。
<?php const WIN_LOSE_TABLE = [ 'グー' => ['チョキ' => 1, 'パー' => -1], 'チョキ' => ['パー' => 1, 'グー' => -1], 'パー' => ['グー' => 1, 'チョキ' => -1], ]; $printer = fn(string $my_hand, string $your_hand, int $result): string => sprintf("[%s vs %s] %s", $my_hand, $your_hand, match ($result) { -1 => 'あなたの負け', 0 => 'あいこ', 1 => 'あなたの勝ち', } ); foreach (['グー', 'チョキ', 'パー'] as $my_hand) { foreach (['グー', 'チョキ', 'パー'] as $your_hand) { echo battle($my_hand, $your_hand, $printer), PHP_EOL; } } /** * 尋常に勝負 */ function battle(string $my_hand, string $your_hand, Closure $printer): string { return $printer($my_hand, $your_hand, WIN_LOSE_TABLE[$my_hand][$your_hand] ?? 0); }大事なのは
const WIN_LOSE_TABLE
と、それを取り出すところでWIN_LOSE_TABLE[$my_hand][$your_hand] ?? 0
してるところ。match ($result)
のところは勝ち・負け・あいこの状態を文字列に変換してるだけ。これだけだと見映えがしないので
絵文字を使いましょう
<?php namespace Janken; use Closure; const WIN_LOSE_TABLE = [ 'グー' => ['チョキ' => 1, 'パー' => -1], 'チョキ' => ['パー' => 1, 'グー' => -1], 'パー' => ['グー' => 1, 'チョキ' => -1], ]; const EMOJI_TABLE = [ 'グー' => '✊', 'チョキ' => '✌', 'パー' => '?', ]; $printer = fn(string $my_hand, string $your_hand, int $result): string => sprintf("[%s vs %s] %s", EMOJI_TABLE[$my_hand], EMOJI_TABLE[$your_hand], match ($result) { -1 => 'あなたの負け', 0 => 'あいこ', 1 => 'あなたの勝ち', } ); foreach (['グー', 'チョキ', 'パー'] as $my_hand) { foreach (['グー', 'チョキ', 'パー'] as $your_hand) { echo battle($my_hand, $your_hand, $printer), PHP_EOL; } } function battle(string $my_hand, string $your_hand, Closure $printer): string { return $printer($my_hand, $your_hand, WIN_LOSE_TABLE[$my_hand][$your_hand] ?? 0); }動作確認: https://3v4l.org/MFqsQ
やりましたね!