- 投稿日:2021-06-15T23:41:10+09:00
【PHP】php課題12-12 Dogクラスを定義し、インスタンス「ハチ($hati)」を生成してintroduceメソッドを実行させる
要件に従いDogクラスを定義し、インスタンス「ハチ($hati)」を生成してintroduceメソッドを実行する。 ハチのプロパティ 名前: ハチ 体重: 20kg 体長: 70cm Dogインスタンスのプロパティ Dogインスタンスは以下の private プロパティを持つ。 name: 名前を表す文字列。 weight: 体重をkg単位で表す数値。 height: 体長をcm単位で表す数値。 コンストラクタ Dogインスタンスは上記のname, weight, height を設定するコンストラクタを持つ。 Dogインスタンスのメソッド Dogインスタンスが持つメソッドは以下の通り。 privateメソッド introduce_name: 名前を教えるメソッド。「私の名前は[名前]です。」と出力。 introduce_weight: 体重を教えるメソッドです。「私の体重は[体重]kgです。」と出力した後、 体重に応じて10kg未満であれば「小型犬です。」、 10kg以上25kg未満であれば「中型犬です。」、 25kg以上であれば「大型犬です。」と出力。 introduce_height: 体長を教えるメソッド。 「私の体長は[体長]cmです」と出力します。 publicメソッド shout: 「ワン!」と出力するメソッドです。 introduce: introduce_name, introduce_weight, introduce_height メソッドを順に実行し、最後にshoutメソッドを実行して終了します。 <?php // 猫型ロボットの製造機械、Catクラスを作る class Dog { // public $name; private $name; private $weight; private $height; public function __construct($name, $weight,$height){ $this->name = $name; $this->weight = $weight; $this->height = $height; } public function introduce(){ $this->introduce_name(); $this->introduce_weight(); $this->introduce_height(); $this->shout(); } private function introduce_name(){ echo '名前は'. $this->name. 'です<br>'; } private function introduce_weight(){ echo '私の体重は'. $this->weight.'kgです。<br>'; if($this->weight < 10){ echo '小型犬です。<br>'; }elseif($this->weight < 25){ echo '中型犬です。<br>'; }else{ echo '大型犬です。<br>'; } } private function introduce_height(){ echo '私の体長は'. $this->height.'cmです<br>'; } public function shout(){ echo 'ワン!<br>'; } } $hati = new Dog('ハチ', 40,70); // $hati->name = "ssss"; $hati->introduce();
- 投稿日:2021-06-15T23:05:18+09:00
【PHP】staticメソッドを使用したサンプルソース
<?php class Cat { // privateに変更 private static $count = 0; // 現在までのインスタンスの作成数を出力するメソッド public static function say_count(){ echo '今までに作成したCatインスタンスの数: ' . self::$count; } public $name; public $age; public function __construct($name, $age){ $this->name = $name; $this->age = $age; // static プロパティ $count を1増やす self::$count++; } public function shout(){ echo 'にゃー!<br>'; } public function introduce(){ echo '名前は' . $this->name . '、' . $this->age . '歳です'; $this->shout(); } } $cats = []; $taro = new Cat('太郎', 3); $cats[] = $taro; $mike = new Cat('ミケ', 5); $cats[] = $mike; $tama = new Cat('タマ', 4); $cats[] = $tama; foreach($cats as $cat){ $cat->shout(); $cat->introduce(); } // staticプロパティの利用 // echo '今までに作成したCatインスタンスの数: ' . Cat::$count; // static メソッドの利用 Cat::say_count();
- 投稿日:2021-06-15T22:49:23+09:00
laradockで複数プロジェクトの環境構築をする
【Laravel】Laradockで複数プロジェクトを動かす手順 https://laradock.io/getting-started/#B 公式と上の記事を参考にlaradockの開発環境をしてみました。 その記録として、残しておこうと思います。 1プロジェクトに1laradockで増やしていくことを前提とした内容です。 workspace |_app |_laradock |_source この環境構築の際にものすごくエラーがでたので、エラーだけをまとめた記事を別で作りました。合わせて参考にしていただければ幸いです。 laradock環境構築のエラーと解決策 Laradockをgit cloneする $ git clone https://github.com/Laradock/laradock.git laradock envファイルを編集 まずは基本的な設定から。 $ cd laradock $ cp .env.example .env 以下の部分を変えてください。バージョンはお好みで。 APP_CODE_PATH_HOST=../app DATA_PATH_HOST=.app/data COMPOSE_PROJECT_NAME=app PHP_VERSION=7.3 MYSQL_VERSION=5.7 confファイルを編集 $ cd nginx/sites $ cp laravel.conf.example app.conf コピーして内容を変更します。 下記引用しました。こちらに沿ってください。 larave.conf.exampleではlaravelというフォルダで動かすようにかかえれているので、そのlaravel部分をproject1という風に書き換えます。 またproject1.testだとchromeで動かなかったりするのでproject1.localとしています。 (https://pensuke.work/posts/laradock-multiproject/) また「hostsの設定を行う」という手順もありますが、現在設定はしていますが、自分の環境では上手くいってないので、割愛します。 (個別で設定してるのにlocalhostでも表示されてしまうのです…) こちらも引用させていただきます。 Nginxでdocker上のサイト設定は行いましたが、これだけではローカルPC上で http://project1.local/にアクセスしてもなにも表示されません。 hostsの最下部に追記しましょう。 編集ファイル: /private/etc/hosts 127.0.0.1 project1.local ::1 project1.local DBの設定 $ cd mysql/docker-entrypoint-initdb.d/ $ cp createdb.sql.example createdb.sql この中のファイルを以下のように編集します CREATE DATABASE IF NOT EXISTS `app` COLLATE 'utf8_general_ci' ; GRANT ALL ON `app`.* TO 'default'@'%' ; コメントアウトを外し、2ヶ所変更します。 mysqlコンテナを立ち上げ、DBの内容を確認しましょう $ docker-compose up -d mysql $ docker exec -it laradock_mysql_1 bash # mysql -u root -p Enter password: Server version: 5.7.34 MySQL Community Server (GPL) mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | default | | mysql | | performance_schema | | sys | | app | +--------------------+ 6 rows in set (0.00 sec) 自分の作りたいDB名がはいってたらOKです。 アプリを作る or プロジェクトをgit cloneして残りのコンテナを起動 自分の場合はgit cloneしました。 新しく作る場合は他でも紹介されているので割愛します。 $ docker-compose exec --user=laradock workspace bash # git clone URL git cloneしてきた場合は.envファイルをコピー&venderファイルを作成してください $ docker exec -it laradock_workspace_1 bash # cd app # cp .env.example .env # composer install プロジェクト内の.envファイルがテキストエディタなどで保存できない場合は、 権限を確認して、変更してください。 これを # ls -l total 1604 drwxr-xr-x 14 root root 4096 Jun 3 10:27 app -rw-r--r-- 1 root root 1686 Jun 3 10:27 artisan 省略 こう # chown -R laradock:laradock /var/www # ls -l total 1604 drwxr-xr-x 14 laradock laradock 4096 Jun 3 10:27 app -rw-r--r-- 1 laradock laradock 1686 Jun 3 10:27 artisan 省略 laravelプロジェクトの.envファイルを編集 laradockのmysqlコンテナを立ち上げる前に設定したDB名にしましょう。 DB_DATABASE=app migrateする /var/www# cd app # php artisan migrate マイグレーションできない時はmysqlの権限を確認してください $ docker exec -it laradock_mysql_1 bash # /var/lib/mysql# ls -l ちなみにファイル構成はこんな感じでつくったDBが入ってます root@5120d5325290:/var/lib/mysql# ls auto.cnf ca.pem client-key.pem ib_buffer_pool ib_logfile1 ibtmp1 performance_schema public_key.pem server-key.pem app ca-key.pem client-cert.pem default ib_logfile0 ibdata1 mysql private_key.pem server-cert.pem sys 権限がmysql以外になってたら、mysqlに変えましょう -rw-r----- 1 1000 1000 56 Jun 3 09:47 auto.cnf -rw------- 1 1000 1000 1676 Jun 3 09:47 ca-key.pem -rw-r--r-- 1 1000 1000 1112 Jun 3 09:47 ca.pem 省略 # chown -R mysql:mysql /var/lib/mysql root@5120d5325290:/var/lib/mysql# ls -l total 188484 -rw-r----- 1 mysql mysql 56 Jun 3 09:47 auto.cnf -rw------- 1 mysql mysql 1676 Jun 3 09:47 ca-key.pem これでOK! 再度migrateしましょう webページが表示されるか確認 ここまできたらあと少しです! コンテナを全て立ち上げ、webページが表示されるか見てみましょう。 $ docker-compose up -d nginx mysql APP KEYを作成する 最後にlaravelのエラー画面が出ていたら、APP KEYを作成してください。 # php artisan key:generate Application key set successfully. # php artisan config:cache Configuration cache cleared! Configuration cached successfully! これでlaravelが使えるようになっているはずです!
- 投稿日:2021-06-15T21:53:34+09:00
laravelでdoctrine
はじめに https://github.com/laravel-doctrine/orm を使ってlaravelでDoctrineしてみた。 インストール Laravelプロジェクトの作成。 curl -s "https://laravel.build/laravel-doctrine" | bash cd laravel-doctrine 依存ライブラリのインストール。 sail composer req doctrine/inflector:^1.4 laravel-doctrine/orm laravel-doctrine/migrations sail artisan vendor:publish --tag="config" EntityとRepositoryの作成 Entity larave-doctrineを使う時はEloquentのモデルを用いません。代わりにapp/Entities/以下にモデルの代わりのクラスであるエンティティを作成します。エンティティにはアノテーションでカラム情報などのメタデータを付与する必要がありますが、基本的にはPOPOとして定義できるのがEloquentとの大きな違いです。また、カラムとのマッピングはymlやxmlで定義することもでき、そのようにするとエンティティは純粋なPOPOになります。メタデータに関してはこちらを参照ください。 laravelのレスポンスに直接渡せるようにIlluminate\Contracts\Support\Arrayableをimplementsしておくと便利です。 app/Entities/User.php <?php namespace App\Entities; use Doctrine\ORM\Mapping as ORM; use Illuminate\Contracts\Support\Arrayable; /** * @ORM\Entity */ class User implements Arrayable { /** * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ protected $id; /** * @ORM\Column(type="string") */ protected $name; public function getId() { return $this->id; } public function getName() { return $this->name; } public function setName($name): self { $this->name = $name; return $this; } public function toArray() { return [ 'id' => $this->id, 'name' => $this->name, ]; } } Repository Doctrineを使った設計では、基本的に一つのエンティティに対して一つのリポジトリを作ります。リポジトリクラスでは以下のようにDoctrine\ORM\EntityRepositoryを継承し、コンストラクタで親クラスにEntityManagerInterfaceとメタデータを渡します。 また、Doctrine\ORM\EntityRepositoryにはfind()、findAll()、findBy()、findOneBy()、count()などのよく使うメソッドがあらかじめ定義されているので、これらのメソッドは自分で実装する必要がありません。 app/Repository/UserRepository.php <?php namespace App\Repository; use App\Entities\User; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; class UserRepository extends EntityRepository { public function __construct(EntityManagerInterface $em) { parent::__construct($em, $em->getClassMetadata(User::class)); } } マイグレーションの作成と実行 Eloquentを使う時はマイグレーションファイルは自前で書く必要がありました。Doctrineでは、エンティティに付与したメタデータと実際のDBとの差分を見て自動でマイグレーションを作ってくれます(めちゃくちゃ便利)。 sail artisan doctrine:migrations:diff sail artisan doctrine:migrations:migrate 簡単な例 コントローラーを作成し、ルートに登録します。 routes/api.php <?php use App\Http\Controllers\UserController; use Illuminate\Support\Facades\Route; Route::group(['auth:api'], function () { Route::resource('users', UserController::class)->only([ 'index', 'store', 'show', 'destroy' ]);; }); コントローラーの各メソッドでは、先ほど作ったリポジトリをメソッドインジェクションできます。リポジトリにはfindXXX()系のメソッドが既に定義されているので、簡単な検索ならメソッドを追加するまでもありません。 リポジトリはデータソースとしての役割のみ持っていますので、「データの更新」や「データの削除」などDBへの書き込みはエンティティマネージャーを通して行う必要があります。エンティティマネージャーはlaravel-doctrineでFacadeとして提供されているのでEntityManager::persist($user)やEntityManager::flush()のように使うことも可能です。 app/Http/Controllers/UserController.php <?php namespace App\Http\Controllers; use App\Entities\User; use App\Repository\UserRepository; use Doctrine\ORM\EntityManagerInterface; use Illuminate\Http\JsonResponse; class UserController extends Controller { public function index(UserRepository $repository): JsonResponse { $users = $repository->findAll(); return response()->json(collect($users)); } public function store(EntityManagerInterface $entityManager): JsonResponse { $user = new User(); $user->setName('test'); $entityManager->persist($user); $entityManager->flush(); return response()->json($user); } public function show($id, UserRepository $repository): JsonResponse { $user = $repository->find($id); return response()->json($user); } public function destroy($id, UserRepository $repository, EntityManagerInterface $entityManager): JsonResponse { $user = $repository->find($id); if (!$user) { return response()->json([], 404); } $entityManager->remove($user); $entityManager->flush(); return response()->json(); } } こんな感じに動きます。 $ curl http://localhost/api/users [] $ curl http://localhost/api/users -X POST {"id":1,"name":"test"} $ curl http://localhost/api/users -X POST {"id":2,"name":"test"} $ curl http://localhost/api/users -X POST {"id":3,"name":"test"} $ curl http://localhost/api/users/2 [{"id":2,"name":"test"}] $ curl http://localhost/api/users/2 -X DELETE [] $ curl http://localhost/api/users [{"id":1,"name":"test"},{"id":3,"name":"test"}] 感想 Eloquentを使うメリットはやはりLaravelとの親和性がとても高いところです。しかし、リポジトリパターンを使おうとすると、Eloquentの便利メソッド系はリポジトリ内でしか使えないという暗黙のルールができてしまいます。それなら最初からリポジトリを考慮した設計のORM使った方がよくない?と思い今回laravel-doctrineを触ってみました。 まだ本当に基本的なことしかしてないですが、ORMとしての機能はまったく問題なさそうな感じがします。また、エンティティにはIlluminate\Contracts\Support\Arrayableをはじめとするインターフェイス類をimplementsさせればLaravelとの親和性も思ったほど低くならないかも?とも思いました。 もう少し色んなパターンで触ってみます。
- 投稿日:2021-06-15T20:03:26+09:00
laradock環境構築のエラーと解決策
laradockで環境構築したらエラーの見本市かな?というくらいにエラーが出たのでその時のエラーと解決策をまとめました。 色んなエラーが出たので参考までにどうぞ! プロジェクトフォルダをVScode等で書き換えようとするとエラーになる でもvimだったら変更できる、という方は権限の問題です。 workspaceのソースコードのあるディレクトリで、権限を確認してください /var/www/app$ ls -l /var/www/app$ ls -l total 1600 drwxr-xr-x 14 root root 4096 Jun 3 10:27 app -rw-r--r-- 1 root root 1686 Jun 3 10:27 artisan drwxr-xr-x 3 root root 4096 Jun 3 10:27 bootstrap -rw-r--r-- 1 root root 2153 Jun 3 10:27 composer.json -rw-r--r-- 1 root root 313747 Jun 3 10:27 composer.lock drwxr-xr-x 2 root root 4096 Jun 3 10:27 config drwxr-xr-x 5 root root 4096 Jun 3 10:27 database -rw-r--r-- 1 root root 1275 Jun 3 10:27 package.json (省略) このような感じでrootになっていたら全てのファイルをlaradockに変える必要があります。 これで解決 /var/www# chown -R laradock:laradock /var/www /var/www以下の権限をlaradockに変更することで、コンテナ外のVScodeからでも保存できます。 root@09e56af218da:/var/www/source# ls -l total 1604 drwxr-xr-x 14 laradock laradock 4096 Jun 3 10:27 app -rw-r--r-- 1 laradock laradock 1686 Jun 3 10:27 artisan drwxr-xr-x 3 laradock laradock 4096 Jun 3 10:27 bootstrap -rw-r--r-- 1 laradock laradock 2153 Jun 3 10:27 composer.json -rw-r--r-- 1 laradock laradock 313747 Jun 3 10:27 composer.lock drwxr-xr-x 2 laradock laradock 4096 Jun 3 10:27 config drwxr-xr-x 5 laradock laradock 4096 Jun 3 10:27 database -rw-r--r-- 1 laradock laradock 1275 Jun 3 10:27 package.json (省略) 良い感じ! しかしこのようになっている人は、コンテナに入るコードが違っている可能性があります。laradockユーザーでコンテナに入ってください。 $ docker-compose exec --user=laradock workspace bash MySQLのインストールバージョンを間違えた&Permission deniedで.laradock/data/mysqlが消せない 人間、間違えてはいけない時こそ間違えるんですよねぇ(遠い目) ほんとは5.7がよかったのにやってしまった $ docker-compose exec mysql mysql --version mysql Ver 8.0.23 for Linux on x86_64 (MySQL Community Server - GPL) $ cat .env | grep DATA_PATH_HOSTで探した場所にdockerの情報がはいっていて、これを消さないとここから過去のデータを引き継いでしまいます。 Dockerさんすごい賢い $ cat .env | grep DATA_PATH_HOST DATA_PATH_HOST=~/.laradock/data $ rm -rf ~/.laradock/data/mysqlこれができなかったら $ sudo rm -rf ~/.laradock/data/mysqlこっちで消し去ってください。 $ docker rmi laradock_mysql $ docker rm (mysqlコンテナID) $ docker ps -a mysqlコンテナもイメージも消し去り、それを確認してください そして今度は、laradockの.envファイルを正しく修正してください。 その後もう一度mysqlコンテナを立ち上げて、バージョンを確認してください。 $ docker-compose up -d mysql $ docker-compose exec mysql mysql --version mysql Ver 14.14 Distrib 5.7.31, for Linux (x86_64) using EditLine wrapper 作るDBの名前を間違えた $ docker-compose up -d mysql $ docker exec -it laradock_mysql_1 bash # mysql -u root -p Enter password: mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | default | | dev_db_1 | | mysql | | performance_schema | | sys | +--------------------+ 6 rows in set (0.00 sec) DB作ったし、内容確認… dev_db_1じゃないの、違う名前がいいの… そんな時も解決方法は上記MySQLのバージョンを間違えた場合と同じようにイメージとdataを消し去ってから再度コンテナを作り直して下さい。 Fatal error: require(): Failed opening required '/project/vendor/autoload.php' (include_path='.:') in /project/artisan on line 18 いざlaravel動かすぞ〜と思った矢先、動かせない そんな時はこれで解決 /var/www/app# $ composer install git cloneしたアプリだとvenderがないのでエラーが起こるようです。 インストールできたらOK! Package manifest generated successfully. 87 packages you are using are looking for funding. Use the `composer fund` command to find out more! SQLSTATE[HY000]: General error: 1017 Can't find file: './app/migrations.frm' (errno: 13 - Permission denied) マイグレーションしようとしたらこんなエラーが SQLSTATE[HY000]: General error: 1017 Can't find file: './app/migrations.frm' (errno: 13 - Permission denied) こちらも権限がないため書き込みができない状態になっています。 自分のMySQLディレクトリの権限がどうなってるか確認しましょう $ docker exec -it laradock_mysql_1 bash # cd var # cd lib # cd mysql /var/lib/mysql# ls auto.cnf ca.pem client-key.pem ib_buffer_pool ib_logfile1 ibtmp1 performance_schema public_key.pem server-key.pem app ca-key.pem client-cert.pem default ib_logfile0 ibdata1 mysql private_key.pem server-cert.pem sys データベースの内容と諸々のファイルがでてきたらここのディレクトリです。 /var/lib/mysql# ls -l total 188484 -rw-r----- 1 1000 1000 56 Jun 3 09:47 auto.cnf -rw------- 1 1000 1000 1676 Jun 3 09:47 ca-key.pem -rw-r--r-- 1 1000 1000 1112 Jun 3 09:47 ca.pem -rw-r--r-- 1 1000 1000 1112 Jun 3 09:47 client-cert.pem -rw------- 1 1000 1000 1680 Jun 3 09:47 client-key.pem drwxr-x--- 2 1000 1000 4096 Jun 3 09:47 default -rw-r----- 1 1000 1000 1359 Jun 3 09:47 ib_buffer_pool -rw-r----- 1 1000 1000 50331648 Jun 3 10:24 ib_logfile0 省略 全部が1000になっていますね、これを全てmusqlに変更しましょう。 これで解決 # chown -R mysql:mysql /var/lib/mysql 権限を確認 # ls -l total 188484 -rw-r----- 1 mysql mysql 56 Jun 3 09:47 auto.cnf -rw------- 1 mysql mysql 1676 Jun 3 09:47 ca-key.pem 省略 これでいけそうですね。 mysqlコンテナから抜けてworkspaceコンテナからapp直下にはいり、migrateを実行してください docker exec -it laradock_workspace_1 bash # php artisan migrate webページを開くと、No application encryption key has been specified.となる workspaceコンテナの内部で、APP KEYを作成してください。 # php artisan key:generate Application key set successfully. # php artisan config:cache Configuration cache cleared! Configuration cached successfully! 参考 【Laravel】Laradockで複数プロジェクトを動かす手順 https://laradock.io/getting-started/#B Laradock環境MySQLで新しいDBを追加する Laradock で mysql がうまく動作しない時は ( mysqlコンテナが起動しない、migration 出来ない) [MySQL]権限の確認と付与 hostsファイルの場所 Windows10 No application encryption key has been specified.となったときの対応方法 MySQL, copying tables files gives rise to “ERROR 1017 (HY000): Can't find file:” even though its there there ERROR 1017 (HY000): Can’t find file: ‘./….frm’ (errno: 13) – MySQL error. How to fix? mysqlを5.5 -> 5.6にバージョンアップしたら、Can't find file: './mysql/plugin.frm' (errno: 13 - Permission denied)で起動こける 既にあるLaradock環境をそのままに複数のLaradock環境を構築する(Windows) docker-composeとdockerコマンドまとめ Laradock nginxでの404 not found Laravelのエラー:Fatal error: require(): Failed opening required '/project/vendor/autoload.php' (include_path='.:') in /project/artisan on line 18 LaradockでLaravel+Docker環境構築(mac)
- 投稿日:2021-06-15T18:27:59+09:00
【PHP】str_replace()とstrtr()の挙動の違い
はじめに PHPで複数の文字列を一度に置換したい場合に利用するstr_replace()とstrtr()ですが、挙動が少し違ったので書き残しておきます。 要件 「東京都新宿区新宿3丁目」という文字列があって、新宿区は<span>新宿区</span>に、新宿は<b>新宿</b>に置換したいとします。 「東京都<span>新宿区</span><b>新宿</b>3丁目」 1. str_replace() 最初に思いついた方法が以下になります。 str_replace $str = '東京都新宿区新宿3丁目'; $array = [ '新宿区' => '<span>新宿区</span>', '新宿' => '<b>新宿</b>', ]; echo str_replace(array_keys($array), array_values($array), $str); // '東京都<span><b>新宿</b>区</span><b>新宿</b>3丁目' str_replace()では、配列の先頭から順番に文字列を置換します。 まず「東京都新宿区新宿3丁目」の新宿区という文字列を置換して 「東京都<span>新宿区</span>新宿3丁目」になります。 次に「東京都<span>新宿区</span>新宿3丁目」から新宿を探してもう一度置換を行うので 「東京都<span><b>新宿</b>区</span><b>新宿</b>3丁目」となります。 2. str_replace() 配列の順番を変えてみます。 str_replace $str = '東京都新宿区新宿3丁目'; $array = [ '新宿' => '<b>新宿</b>', '新宿区' => '<span>新宿区</span>', ]; echo str_replace(array_keys($array), array_values($array), $str); // '東京都<b>新宿</b>区<b>新宿</b>3丁目' 念のためもう一度。str_replace()では、配列の先頭から順番に文字列を置換します。 この場合、「東京都新宿区新宿3丁目」の新宿という文字列を置換して 「東京都<b>新宿</b>区<b>新宿</b>3丁目」になります。 置換後の文字列には’新宿区’という文字列は無いので、そのまま出力されます。 3. strtr() strtr $str = '東京都新宿区新宿3丁目'; $array = [ '新宿' => '<b>新宿</b>', '新宿区' => '<span>新宿区</span>', ]; echo strtr($str, $array); // '東京都<span>新宿区</span><b>新宿</b>3丁目' これで欲しい結果が取得できました。 strtr()`の場合、配列の順番は関係がなく、キーの文字列の長いものから順に調べて置換をし、一度置換された部分の文字列は置換対象から外れるようです。 新宿と新宿区では、もちろん「新宿区」の方が文字列としては長いので、先に「新宿区」が「<span>新宿区</span>」に置換されます。 その後、置換された新宿区は対象外になるので、「東京都<span>新宿区</span>新宿3丁目」が置換されて 東京都<span>新宿区</span><b>新宿</b>3丁目となります。 おわりに 今回のは記事はどちらの関数の方が良いかという話ではなく、要件によって使い分けが必要だと思います。 複数の文字列の置換をしたい時にこの記事を思い出してもらえればと思います。 参考
- 投稿日:2021-06-15T17:38:56+09:00
CakePHP4でトランザクションを使う
CakePHP4でトランザクションを使う! use Cake\Datasource\ConnectionManager; use Cake\Core\Exception\Exception; $connection = ConnectionManager::get('default'); $connection->begin(); $users = TableRegistry::getTableLocator()->get('users'); $articles = TableRegistry::getTableLocator()->get('articles'); $users->setConnection($connection); $articles->setConnection($connection); try { $user = $users->newEmptyEntity(); $user->email = 'hogehoge@hogehoge.com'; $user->password = 'passpass'; $users->save($user, ['atomic' => false]); $article = $articles->newEmptyEntity(); $article->user_id = $user->id; $article->title = 'タイトル'; $article->body = '本文'; $articles->save($article, ['atomic' => false]); $connection->commit(); } catch (Exception $e) { $connection->rollback(); throw $e; }
- 投稿日:2021-06-15T14:40:06+09:00
PHPでセッションを利用してみた
以前 クッキー、セッションについてまとめてみましたが、実装していなかったので 今回は例を用いて実装していこうと思います。 クッキーとセッションって何?[図あり] 画面 内容 ■http://localhost/session/session1.php ・初回の場合「初回の訪問です。」表示。 ・2回目以降「訪問回数」表示。 ■http://localhost/session/session2.php session1.phpの「ログアウトする」ボタンを押下すると表示。 セッションを削除します。 ■http://localhost/session/session3.php session2.phpの「ログアウトの確認」ボタンを押下すると表示。 現在のセッションの状態を確認できる。 実装 session1.php <?php session_start(); ?> <html> <head> <title>PHP TEST</title> </head> <body> <?php //東京の時刻に設定 date_default_timezone_set('Asia/Tokyo'); if (!isset($_SESSION["visited"])) { print('初回の訪問です。セッションを開始します。'); //セッション変数は複数保存可能 $_SESSION["visited"] = 1; $_SESSION["date"] = date('Y年m月d日 H時i分s秒'); } else { $visited = $_SESSION["visited"]; $visited++;//更新するたびに訪問回数が増えるように print('訪問回数は' . $_SESSION["visited"] . 'です。<br>'); $_SESSION["visited"] = $visited; if (isset($_SESSION["date"])) { print('前回の訪問日時は' . $_SESSION["date"] . 'です。<br>'); } $_SESSION["date"] = date('Y年m月d日 H時i分s秒'); } ?> <p> <a href="./session2.php">ログアウトする</a> </p> </body> </html> session2.php <?php session_start(); ?> <html> <head> <title>PHP TEST</title> </head> <body> <?php print('セッション変数の一覧を表示します。<br>'); print_r($_SESSION); print('<br>'); print('セッションIDを表示します。<br>'); //デフォルトでPHPSESSIDが表示 print($_COOKIE["PHPSESSID"] . '<br>'); print('<p>ログアウトします</p>'); //セッション変数を全て解除 $_SESSION = array(); if (isset($_COOKIE["PHPSESSID"])) { setcookie("PHPSESSID", '', time() - 1800, '/'); } //セッションを破棄する session_destroy(); ?> <p> <a href="./session3.php">ログアウトの確認</a> </p> </body> </html> session3.php <?php session_start(); ?> <html> <head> <title>PHP TEST</title> </head> <body> <?php print('セッション変数の確認をします。<br>'); if (!isset($_SESSION["visited"])) { print('セッション変数visitedは登録されていません。<br>'); } else { print($_SESSION["visited"] . '<br>'); } print('セッションIDの確認をします。<br>'); if (!isset($_COOKIE["PHPSESSID"])) { print('セッションは登録されていません。<br>'); } else { print($_COOKIE["PHPSESSID"] . '<br>'); } ?> <a href="./session1.php">ログインする</a> </body> </html>
- 投稿日:2021-06-15T06:39:20+09:00
【Php】Csrf対策ー学習ノート
初めに phpのCsrf対策について学習した内容のoutput用記事です。 ※内容に間違いなどがある場合はご指摘をよろしくお願いします。 ※こちらの記事はあくまでも個人で学習した内容のoutputとしての記事になります。 前回の記事:https://qiita.com/redrabbit1104/items/a6e57aa1fd1771ef90ff Csrfとは Csrfはクロスサイトリクエストフォージェリ(Cross-Site Request Forgery)の略で、本物そっくりなWebサイトを用意しユーザーからのログインなどを誘導。不正なリクエストなどを要求されたりして、利用者の個人情報や公開されてはいけないデータなどを盗み取る攻撃手法です。 対策方法→ sessionを利用する \$_POSTや\$_GETなどのHTTP通信に使われるスーパグローバル変数は一度きりのものであり利用ごは削除されてしまいます。それに対して\$_SESSIONはずっとデータがずっと残り続けます。この\$_SESSIONを合言葉として使えば本物かどうかをチェックすることができます。 使ってみる sessionを使うにはまずsession_start()関数を使ってsessionをスタートさせる必要があります。これをphpファイルの冒頭に記述します。 session_start(); 次に合言葉を作成します。前回作った入力フォームの中から入力の際に確認を取りたいので、入力画面に合言葉を生成することにします。 random_bytes()関数 phpのマニュアルには 暗号論的に安全な、疑似ランダムなバイト列を生成する となっています。24~32までの数字を引数にして作成しますが、今回は32にしてみます。そして中身が気になるので、echoでブラウザーに表示してみます。 <?php echo random_bytes(32); ?> 訳のわからない機械言語みたいなものが表示されます。 bin2hex()関数 これを人が読めるような数字にしたいので、bin2hex()関数を使って16進数に変えます。 <?php echo bin2hex(random_bytes(32)); ?> 英語と数字が混ざっている16真数の数字になりました。(0~9,a~fまでの英数字) この数字がtokenになります。これを\$csrfTokenという変数に格納します。 $csrfToken = bin2hex(random_bytes(32)); $_SESSIONに$csrfTokenを保存しておく \$_SESSIONは連想配列の構造になっています。これはsession_start()関数によって生成されます。この\$_SESSIONにキー名を'csrfToken'にして、先ほど生成した16進数の英数字$csrfTokenを格納します。そして、isset()関数を使い\$_SESSION['csrfToken']がセットされていない場合に\$csrfTokenを格納するようにします。 if(!isset($_SESSION['csrfToken'])){ $csrfToken = bin2hex(random_bytes(32)); $_SESSION['csrfToken'] = $csrfToken; } また、\$_SESSION['csrfToken']は長いので、$tokenという名前の変数に再代入します。 $token = $_SESSION['csrfToken']; 入力画面で$tokenをhiddenタイプで$tokenを渡しておく typeを"hidden"にしフォームに表示されない形で\$tokenの値をpostします。nameは"csrf"にし、valueにはphpのechoで\$tokenを渡します。 <input type="hidden" name="csrf" value="<?php echo $token; ?>"> 完成した入力画面は以下の通り。これで$_SESSIONにも$_POSTにも同じ値が格納されていることになります。 <?php if ($page_flag === 0) : ?> <?php if (!isset($_SESSION['csrfToken'])) { $csrfToken = bin2hex(random_bytes(32)); $_SESSION['csrfToken'] = $csrfToken; } $token = $_SESSION['csrfToken']; ?> 入力画面 <form method="POST" action="csrf.php"> 名前 <input type="text" name="input_name"> <br> <input type="submit" name="btn_submit" value="submit"> <input type="hidden" name="csrf" value="<?php echo $token; ?>"> </form> <?php endif; ?> 確認画面で$_POSTの値と$_SESSIONの値が同じ場合にのみ、画面表示を行う 入力画面のPOSTの値として\$tokenを渡しています。それと$_SESSIONの中に保存されている値が一致する場合のみ、確認画面の処理が行われるようにします。一致しないということは本物のWebサイトの入力画面で発行した\$_SESSIONがないということであり、不正なサイトから誘導されたことになるわけです。 <?php if ($_POST['csrf'] === $_SESSION['csrfToken']) : ?> また、確認画面でもhiddenタイプで\$_POSTの値を渡すようにします。これは後ほど出てくる完了画面で\$_SESSIONの値と$_POSTの値が一致するかどうかを条件分岐によって判断するからです。\$_POSTは一回きりの値なので入力画面で渡したとしても、消えてしまいます。なので、再度hiddenタイプで渡す必要があります。 <input type="hidden" name="csrf" value="<?php echo sp_chars($_POST['csrf']); ?>"> 出来上がった確認画面は以下の通りになります。 <?php if ($page_flag === 1) : ?> <?php if ($_POST['csrf'] === $_SESSION['csrfToken']) : ?> 確認画面 <form method="POST" action="csrf.php"> 名前 <?php echo sp_chars($_POST["input_name"]); ?> <br> <input type="submit" name="btn_confirm" value="confirm"> <input type="hidden" name="input_name" value="<?php echo sp_chars($_POST['your_name']); ?>"> <input type="hidden" name="csrf" value="<?php echo sp_chars($_POST['csrf']); ?>"> </form> <?php endif; ?> <?php endif; ?> 完成画面 \$_POSTの値と\$_SESSIONの値が一致する場合にのみ、完了メッセージが表示するようにします。 <?php if ($_POST['csrf'] === $_SESSION['csrfToken']) : ?> また、unset()メソッドを使い、\$_SESSIONの値を破棄します。理由としてはずっと古いままの\$_SESSIONの値が残り続けていると新しく投稿する際に同じtokenの値になってしまうため、セキュリティ上問題になるからです。 <?php unset($_SESSION['csrfToken']); ?> これで完了画面の出来上がりです。 <?php if ($page_flag === 2) : ?> <?php if ($_POST['csrf'] === $_SESSION['csrfToken']) : ?> 送信完了。 <?php unset($_SESSION['csrfToken']); ?> <?php endif; ?> <?php endif; ?> 参考サイト https://siteguard.jp-secure.com/blog/what-is-csrf https://www.php.net/manual/ja/function.random-bytes.php
- 投稿日:2021-06-15T05:52:51+09:00
PHP基礎メモその3
クラスプロパティ 個々のインスタンスが持つデータ(プロパティ)ではなく、クラスが持つデータをクラスプロパティという。 クラスプロパティは『static』を用いて定義する。 クラスプロパティにアクセスする場合は『クラス名::クラスプロパティ名』のように『::』を用いる。 class Menu { // staticをつけるとクラスプロパティになる public static $count = 4; } // クラスプロパティにアクセス echo 'メニュー'.Menu::$count.'品'; 結果 メニュー4品 self クラス内でクラスプロパティにアクセスする際は『self』という特殊な変数を用いる。 selfは、クラスの中で使うとそのクラス自身のことを指し、『self::$クラスプロパティ名』のように使う。 このselfを用いてコンストラクタの中でクラスプロパティ$countの値を変更する。 class Menu { public static $count = 0; public function __construct(...) { ... self::$count++; // self = Menuクラス } } $menu1 = new Menu('CURRY', ...); $menu2 = new Menu('PASTA', ...); // インスタンスが生成される度に、コンストラクタの中でクラスプロパティ$countに1を足している echo Menu::$count; 結果 2 クラスメソッド $countのアクセス権をprivateにして、$countのゲッターを定義する。 $countのゲッターのように、個々のインスタンスのデータに関係ない処理を行いたい時には 『クラスメソッド』を用いる。クラスメソッドは、『static』を用いて定義し、『クラス名::クラスメソッド名』のように呼び出す。 class Menu { private static $count = 0; // クラス外で$countが書き換えられないようにprivateにする ... // クラスメソッドの定義 public static function getCount() { return self::$count; } } $menu1 = new Menu('CURRY', ...); $menu2 = new Menu('PASTA', ...); // クラスメソッドの呼び出し echo Menu::getCount(); 結果 2 クラスの継承 すでに定義されているクラスのプロパティやメソッドを別のクラスに引き継ぐことを『継承』という。 引き継がれる元のクラスを親クラス継承して新しくできたクラスを子クラスという。 子クラスは親クラスのプロパティやメソッドを全て引き継いだ上で、独自の機能を追加できる。 継承の書き方 class 親クラス名 { ... } class 子クラス名 extends 親クラス名 { ... } 継承の使用例 class Menu { ... public function __construct($name, ...) { $this->name = $name; ... } public function getName() { return $this->name; } ... } // Menuクラスを継承したDrinkクラスを作成 class Drink extends Menu { } // 親クラスのコンストラクタが実行される $coffee = new Drink('COFFEE', ...); // 親クラスのメソッドの呼び出し echo $coffee->getName(); 結果 COFFEE 子クラスに独自のメソッドを定義する class Drink extends Menu { private $type; public function getType() { return $this->type; } public function setTtpe($type) { $this->type = $type; } } $coffee = new Drink('COFFEE', ...); $coffee->setType('ホット'); // 親クラスのインスタンスからは呼び出されない $curry = new Menu('CURRY', ...); $curry->setType('ホット'); instanceof 『instanceof』はあるインスタンスが特定のクラスのインスタンスであるかどうかを判別する時に用いる。 『インスタンス instanceof クラス名』のようにすると、インスタンスが指定したクラスのインスタンスである場合は『true』、 そうでない場合は『false』になる。 $coffee = new Drink('COFFEE', ...); if ($coffee instanceof Drink) { echo $coffee->getName().'はDrinkクラスである'; } if ($coffee instanceof Food) { echo $coffee->getName().'はFoodクラスのインスタンスである'; } 結果 COFFEEはDrinkクラスのインスタンスである protected 親クラスにすでに定義されてる同じ名前のメソッドを子クラスで定義するとメソッドの中身を上書きすることができる。 このようなメソッドの上書きを『オーバーライド』という。 子クラスから親クラスで定義したプロパティにアクセスしたい場合は、そのプロパティのアクセス権を『protected』にする。 アクセス権がprotectedのプロパティには、そのクラス内からと、そのクラスを継承している子クラス内からのみアクセスできる。 親クラス class Menu { protected $name; ... } 子クラス class Drink extends Menu { ... public function __construct($name,...) { $this->name = $name; } } parent オーバークラスの際に親クラスで定義したメソッドを呼び出したいときには、『parent』を用いて『parent::メソッド名』のように呼び出す。 親クラス class Menu { private $name; ... public function __construct($name, $price, $image) { $this->name = $name; $this->price = $price; $this->image = $image; self::$count++; } } 子クラス class Drink extends Menu { ... public function __construct($name, $price, $image, $type) { // 親クラスの__constructメソッドを呼び出す parent::__construct($name, $type, $image); $this->type = $type; } } $coffee = new Drink('COFFEE', ..., 'ホット'); echo $drink->getType(); 結果 ホット
- 投稿日:2021-06-15T00:29:38+09:00
PHP クラスの関数の使い方
<?php class Car { public $color = "塗装前"; public $spead = 0; // 車を走らせるための機能をまとめた関数の部分(クラス側) public function run(){ print "{$this->color}の車で、時速{$this->spead}kmで走行しています。"; } } $myCar = new Car(); // 車を走らせる $myCar->run(); ?>
- 投稿日:2021-06-15T00:29:38+09:00
PHP アロー関数の使い方
<?php class Car { public $color = "塗装前"; public $spead = 0; // 車を走らせるための機能をまとめた関数の部分(クラス側) public function run(){ print "{$this->color}の車で、時速{$this->spead}kmで走行しています。"; } } $myCar = new Car(); // 車を走らせる $myCar->run(); ?>