- 投稿日:2020-02-13T21:13:33+09:00
Moodle 3.8 マニュアル - サーチエンジン最適化
サーチエンジン最適化
このページはアップデートが必要です。そうしてそれが終了したらこのテンプレートを削除してください。
サーチエンジン最適化(SEO)は限定的ですがサーチエンジンの可視性と順位を向上させるためにはいくつかのステップがあります。
サイト管理 > セキュリティ > サイトセキュリティ設定 で「Googleに公開する」を有効にする
きれいで明白なシンプルなサイト名をつける。例えば、[組織名]E-Learning のように。
サイトの記述を設定する(メタデータとして多くのテーマにおいて使用されます)
コースの記述を設定する(フロントページにコースのリンクも設定する。そうでなければ記述はクロールされません。)
SEO にはその他の要因、コンテンツの量、ウェブサイトの通信量や信頼性が作用しています。要するに、多くの関連のある内容と通信量です。
Moodle の設定
サイトセキュリティ設定において「Googleに公開する」設定を 'Yes' にすると Google があなたのサイトにゲストで入れるようになります。加えて、Google サーチを通じてあなたのサイトに来た人々は自動的にゲストログインされます。しかしながら、人びとはコンテンツをあなたのサイトを訪れることなく Google のサイトから読むことができるのみです。
Google があなたのウェブサイトのとなりに表示するテキストはメタ記述のタグとなります。あなたはこの情報を Moodle の 管理 > サイト設定の領域において埋めることができます。
他のサイトから/へのリンク
SEO に主に貢献するのはリンクづけです。評判のよいサイトにリンクすることであなたの順位は上がりますがもっと重要なことに、他のサイトがあなたにリンクすることであなたの順位は上がります。
他のサイトがあなたへリンクすることは大変重要ですが Google は賢いので 30 のサイトを作成してあなたのサイトへリンクするということはできません。Google への評判は重要であり小さいサイトから 100 のリンクは何の評判にもならず例えば www.time.com からの一つのリンクのようにはカウントされません。
その他の方法
もちろん、標準的なことが当てはまります。
- あなたの内容を興味深いものにする
- 関連のあるサイトへのリンクと関連のあるサイトからあなたのサイト(理由のある)へのリンク
- サイトがアクセス可能であるようにすること(Google は JavaScript をオフにしたウェブサーフと同じようなもので見ているので)
- あなたの分野におけるブロガーがあなたのサイトの話をするようにすること(現在のところこれが最もよい方法です)
カテゴリ:requiring updating(翻訳準備中)|管理者
メインページ
- 投稿日:2020-02-13T21:02:35+09:00
Moodle 3.8 マニュアル - テーマディレクトリ
テーマディレクトリ
標準的な Moodle ディストリビューションでは、すべてのテーマは theme/ ディレクトリに配置されます。
典型的なテーマディレクトリ/フォルダの内容のさらなる情報に関してはTheme basics(翻訳準備中)に見つけられます。
サイトのためにテーマのデフォルトディレクトリを変更する
$CFG->themedir を使うことにより、テーマのストレージの場所は変わるかもしれません。この変数により特定されるディレクトリの中に置かれるテーマはテーマセレクタを使用することにより選択できます。
For example, should you wish to place themes in a subdirectory called 'my_moodle_themes', your 'config.php' file might look like this:
例えば、あなたは 'my_moodle_themes' と呼ばれるサブディレクトリにテーマを配置したいとすれば 'config.php' は次のようになるでしょう。$CFG->wwwroot = 'http://my.moodle.site.edu'; $CFG->dirroot = '/var/www/my.moodle.site.edu/public_html'; $CFG->themedir = $CFG->dirroot . '/my_moodle_themes';あるいは web サーバによって読むことが可能な設定にするのならば、例えば、
$CFG->wwwroot = 'http://my.moodle.site.edu'; $CFG->dirroot = '/var/www/my.moodle.site.edu/public_html'; $CFG->themedir = '/home/me/my_moodle_themes';開発者のためのテーマディレクトリ
開発者は テーマディレクトリガイド(翻訳準備中)をフォローすべきです。
関連項目
- 投稿日:2020-02-13T20:15:31+09:00
Moodle 3.8 マニュアル - DNS
DNS
これはインターネットがアルファベットのURL(例えば、
www.bbc.co.uk
)を IP アドレス(例えば241.1.23.7
)に解決するシステムについて言及しています。それは効率的なインターネットの電話帳でありブラウザが必要とされた web ページをホストするマシンに接続できる方法です。あなたの Moodle サイトが外の世界から接続可能であることが重要ですので、あなたのサーバの DNS エントリが重要です。ISP からの基本的なホスティングのプランを使用しているのなら、とても簡単なことです。
- 登録者からドメイン名を得てください(例えば、
www.123-reg.co.jp
)。これは電話帳に登録することと等価です。- ネームサーバにあなたの ISP の登録者の設定をしてください。(これはもしあなたの ISP と登録者が同じならば不必要です。)これはあなたの電話帳に電話番号を書き込むことと等価です。
- 投稿日:2020-02-13T18:14:26+09:00
【Laravel】マルチログイン(ユーザーと管理者)機能
Laravel6で認証をユーザーと管理者など複数にわける場合のメモ。
まずは普通にログイン機能を実装
Laravel 6.0 ログイン機能を実装する - Qiita
Laravel6.0「make:Auth」が無くなった 〜Laravel6.0でのLogin機能の実装方法〜MyMemo - Qiita
ここは参考になる記事が多くあるので説明は省略。
作成したログイン機能に追加
新しく管理者(Admin)の認証機能を追加する。
1. モデルクラスを作成
他のモデルクラスと同じようにマイグレーションファイル(DB)とモデルクラス(Admin.php)を作成。
モデルクラスは
Illuminate\Database\Eloquent\Model
ではなくIlluminate\Foundation\Auth\User
を継承する。Admin.phpuse Illuminate\Foundation\Auth\User as Authenticatable; class Admin extends Authenticatable // ModelではなくAuthenticatable { }2. 認証ファイルに追記
下記の内容を
config/auth.php
に追記する。auth.php'guards' => [ ... 'admin' => [ 'driver' => 'session', 'provider' => 'admin', ], ... ], 'providers' => [ ... 'admin' => [ 'driver' => 'eloquent', 'model' => App\Admin::class, // 1 で作成したモデルを指定 ], ... ], 'passwords' => [ ... 'admin' => [ 'provider' => 'admin', 'table' => 'password_resets', 'expire' => 60, ], ... ],3. Routeに認証機能を追加
web.phpRoute::group(['middleware' => 'auth:admin'], function () { ... });4. ログインしていない場合のリダイレクト先を指定
ログイン処理が必要なURLにログインせずにアクセスした場合のリダイレクト先を
ユーザー、管理者等で分けたい場合は記述する。app/Exceptions/Handler.phpprotected function unauthenticated($request, AuthenticationException $exception) { if($request->expectsJson()){ return response()->json(['message' => $exception->getMessage()], 401); } if (in_array('admin', $exception->guards(), true)) { return redirect()->guest(route('admin.login')); } return redirect()->guest(route('login')); }5. ログイン後にログインページにアクセスした場合のリダイレクト先を指定
app/Http/Middleware/RedirectIfAuthenticatedpublic function handle($request, Closure $next, $guard = null) { switch ($guard) { case 'admin': $redirectPath = '/admin/index'; break; default: $redirectPath = '/index'; break; } if (Auth::guard($guard)->check()) { return redirect($redirectPath); } return $next($request); }6. ログインコントローラー作成
ユーザーの認証に使用している
LoginController
とは別に作成する。LoginControlleruse App\Http\Controllers\Controller; use Illuminate\Support\Facades\Auth; class LoginController extends Controller { use AuthenticatesUsers; protected $redirectTo = 'admin/index'; // ログイン後のリダイレクト先 public function __construct(Request $request) { $this->middleware('guest:admin')->except('logout');; } protected function guard() { return Auth::guard('admin'); } }その他
username
,showLoginForm
などのメソッドはユーザーのLoginControllerと同じように、必要に応じて設定する。参考
【Laravel】マルチログイン(ユーザーと管理者など)機能を設定してみた【体験談】 | もんプロ~問題発見と解決のためのプログラミング〜
- 投稿日:2020-02-13T18:08:54+09:00
textareaでPOSTされたtsvフォーマットのデータを受け取ってパースするメモ
テンポラリファイル作ってうまいことやる。
$temp = tmpfile(); fwrite($temp, $_POST['text']); rewind($temp); $meta = stream_get_meta_data($temp); $files = new SplFileObject($meta['uri']); $files->setFlags( \SplFileObject::READ_CSV | \SplFileObject::READ_AHEAD | \SplFileObject::SKIP_EMPTY | \SplFileObject::DROP_NEW_LINE ); $files->setCsvControl("\t"); foreach($files as $key => $line) { //ここでなんやかんや }
- 投稿日:2020-02-13T17:58:39+09:00
XAMPPでApache起動出来ずにハマった話
※windows10、xamppのバージョン7.4.1で環境を作ってます。
WordPressの環境をローカルで作成しようと思いxamppをダウンロードしてApacheを起動したが動かない・・・。
error.logを確認すると以下エラーが。
PHP Warning: 'vcruntime140.dll' 14.0 is not compatible with this PHP build linked with 14.16 in Unknown on line 0
翻訳してみると、vcruntime140.dllがPHPと互換性がないと言われているように見える。
vcruntime140.dllって何?
色々調べてみると、
https://www.php.net/manual/ja/install.windows.requirements.phpPHPを動かすためにはVisualCのランタイムが必要でvcruntime140.dllというのがランタイムにあたるようだが、今入ってるPHPのバージョンと互換性がないよ!と言われているっぽい。
じゃあどうすれば良いの?
以下ページからVisualStudio2019のMicrosoft Visual C++ 再頒布可能パッケージをインストールして再度Apacheを起動したら起動出来た!
https://visualstudio.microsoft.com/ja/downloads/Apache立ち上げるだけで結構時間がかかってしまった・・・( ;∀;)
エラー内容で調べても出てこなかったので備忘録として。
- 投稿日:2020-02-13T17:27:03+09:00
LaravelでSalesForceのREST APIを利用する
概要
LaravelでSalesForceのREST APIを利用する方法について調査したので記事として残しておく。
ここではよく使いそうなCRUD処理のサンプルを記載する。
※ Laravel特有の部分はあまりないので、他のフレームワークや生PHPでもいけます。前提
- SalesForceのAPIのバージョンはv47.0とする
- Laravel(5.8)のControllerとGuzzle(6.5)を利用する
- access_token、refresh_tokenは取得済とする
- 標準オブジェクトの取引先(Account)を利用する
CRUD処理サンプル
CREATE
/** * SalesForce REST API Create * @Route /sample/sf/api/create * @Method POST */ public function sfApiCreateRecord() { // 環境によって違うので自分の環境の値に変更してください $sfUrl = 'https://hoge.salesforce.com'; $path = '/services/data/v47.0/sobjects/account'; $url = $sfUrl . $path; $token = 'hogefugapiyo'; //tokenを自動で取得してくるような仕組みはこの記事では省略する $postData = [ 'Name' => 'nagi125', 'Description' => 'REST API test', ]; $params = [ 'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer '.$token, ], 'body' => json_encode($postData), ]; $client = new \GuzzleHttp\Client(); $res = $client->request('POST', $url, $params); // 結果の確認 dd($res->getBody()->getContents()); }READ
/** * SalesForce REST API Read * @Route /sample/sf/api/read * @Method GET */ public function sfApiReadRecord() { $sfUrl = 'https://hoge.salesforce.com'; $path = '/services/data/v47.0/sobjects/account/'; $sfId = 'hogefuga'; // このようにSQLの結果も取得できる // $path = '/services/data/v47.0/query/?q=SELECT+Id,Name+From+Account'; $url = $sfUrl . $path . $sfId; $token = 'hogefugapiyo'; $params = [ 'headers' => [ 'Authorization' => 'Bearer '.$token, ], ]; $client = new \GuzzleHttp\Client(); $res = $client->request('GET', $url, $params); // 結果の確認 dd($res->getBody()->getContents()); }UPDATE
/** * SalesForce REST API Update * @Route /sample/sf/api/update * @Method PATCH */ public function sfApiUpdateRecord() { $sfUrl = 'https://hoge.salesforce.com'; $path = '/services/data/v47.0/sobjects/account/'; $sfId = 'hogefuga'; $url = $sfUrl . $path . $sfId; $token = 'hogefugapiyo'; $postData = [ 'Name' => 'nagi125_update', ]; $params = [ 'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer '.$token, ], 'body' => json_encode($postData), ]; $client = new \GuzzleHttp\Client(); $res = $client->request('PATCH', $url, $params); // 結果の確認 dd($res->getBody()->getContents()); }DELETE
/** * SalesForce RestAPI Delete * @Route /sample/sf/api/delete * @Method DELETE */ public function sfApiDeleteRecord() { $sfUrl = 'https://hoge.salesforce.com'; $path = '/services/data/v47.0/sobjects/account/'; $sfId = 'hogefuga'; $url = $sfUrl . $path . $sfId; $token = 'hogefugapiyo'; $params = [ 'headers' => [ 'Authorization' => 'Bearer '.$token, ], ]; $client = new \GuzzleHttp\Client(); $res = $client->request('DELETE', $url, $params); // 結果の確認(deleteの場合、成功時に文字列が返ってこないのでStatusCodeで確認) dd($res->getStatusCode()); }その他
残りの操作について詳しく知りたい場合はREST APIの仕様書を見るとよいです。
- 投稿日:2020-02-13T17:27:03+09:00
PHP(Laravel)でSalesForceのREST APIを利用する
概要
LaravelでSalesForceのRESTAPIを利用する方法について調査したので記事として残しておく。
ここではよく使いそうなCRUD処理のサンプルを記載する。
※ Laravel特有の部分はあまりないので、他のフレームワークや生PHPでもいけます。前提
- SalesForceのAPIのバージョンはv47.0とする
- Laravel(5.8)のControllerとGuzzle(6.5)を利用する
- access_token、refresh_tokenは取得済とする
- 標準オブジェクトの取引先(Account)を利用する
CRUD処理サンプル
CREATE
/** * SalesForce REST API Create * @Route /sample/sf/api/create * @Method POST */ public function sfApiCreateRecord() { // 環境によって違うので自分の環境の値に変更してください $sfUrl = 'https://hoge.salesforce.com'; $path = '/services/data/v47.0/sobjects/account'; $url = $sfUrl . $path; $token = 'hogefugapiyo'; //tokenを自動で取得してくるような仕組みはこの記事では省略する $postData = [ 'Name' => 'nagi125', 'Description' => 'REST API test', ]; $params = [ 'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer '.$token, ], 'body' => json_encode($postData), ]; $client = new \GuzzleHttp\Client(); $res = $client->request('POST', $url, $params); // 結果の確認 dd($res->getBody()->getContents()); }READ
/** * SalesForce REST API Read * @Route /sample/sf/api/read * @Method GET */ public function sfApiReadRecord() { $sfUrl = 'https://hoge.salesforce.com'; $path = '/services/data/v47.0/sobjects/account/'; $sfId = 'hogefuga'; // このようにSQLの結果も取得できる // $path = '/services/data/v47.0/query/?q=SELECT+Id,Name+From+Account'; $url = $sfUrl . $path . $sfId; $token = 'hogefugapiyo'; $params = [ 'headers' => [ 'Authorization' => 'Bearer '.$token, ], ]; $client = new \GuzzleHttp\Client(); $res = $client->request('GET', $url, $params); // 結果の確認 dd($res->getBody()->getContents()); }UPDATE
/** * SalesForce REST API Update * @Route /sample/sf/api/update * @Method PATCH */ public function sfApiUpdateRecord() { $sfUrl = 'https://hoge.salesforce.com'; $path = '/services/data/v47.0/sobjects/account/'; $sfId = 'hogefuga'; $url = $sfUrl . $path . $sfId; $token = 'hogefugapiyo'; $postData = [ 'Name' => 'nagi125_update', ]; $params = [ 'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer '.$token, ], 'body' => json_encode($postData), ]; $client = new \GuzzleHttp\Client(); $res = $client->request('PATCH', $url, $params); // 結果の確認 dd($res->getBody()->getContents()); }DELETE
/** * SalesForce RestAPI Delete * @Route /sample/sf/api/delete * @Method DELETE */ public function sfApiDeleteRecord() { $sfUrl = 'https://hoge.salesforce.com'; $path = '/services/data/v47.0/sobjects/account/'; $sfId = 'hogefuga'; $url = $sfUrl . $path . $sfId; $token = 'hogefugapiyo'; $params = [ 'headers' => [ 'Authorization' => 'Bearer '.$token, ], ]; $client = new \GuzzleHttp\Client(); $res = $client->request('DELETE', $url, $params); // 結果の確認(deleteの場合、成功時に文字列が返ってこないのでStatusCodeで確認) dd($res->getStatusCode()); }その他
残りの操作について詳しく知りたい場合はREST APIの仕様書を見るとよいです。
- 投稿日:2020-02-13T16:05:44+09:00
SalesForceのaccess_token取得方法
概要
SalesForceのAccessToken取得方法をまとめておく。
※refresh_tokenについてはこちらを参照
同時にLaravel + Guzzuleを利用したPHPのサンプルも載せておく。結論
取得方法は複数あるがここでは「ユーザ名パスワードフロー」と「更新トークンフロー」の2種類を記載する。
※ 利用しているAPIのバージョンはv47.0です。共通作業
- 接続アプリケーションの作成をする。(こちらの記事を参照)
ユーザー名・パスワードフロー
パラメータをつけて下記のURLに「POST」でアクセスする。
https://login.salesforce.com/services/oauth2/token
パラメータ 値 grant_type password client_id ***** client_secret ***** username **** password **** 更新トークンフロー
refresh_tokenが必要になるため、refresh_tokenをこちらの記事の方法で取得しておく。
パラメータをつけて下記のURLに「POST」でアクセスする。
https://login.salesforce.com/services/oauth2/token
パラメータ 値 grant_type refresh_token client_id ***** client_secret ***** refresh_token ***** Laravel + Guzzleサンプル
Laravelを利用して書いていますが、機能としてはenvぐらいしか使っていないので生PHPでも普通にいけます。
Guzzleを利用したサンプルと思っていただければと思います。private function refreshSFAccessToken(): string { $path = '/services/oauth2/token'; $url = 'https://login.salesforce.com' . $path; $params = [ 'form_params' => [ 'grant_type' => 'refresh_token', 'client_id' => env('SF_ID'), 'client_secret' => env('SF_SECRET'), 'refresh_token' => env('SF_REFRESH_TOKEN'), ] ]; $client = new \GuzzleHttp\Client(); $resJson = $client->request('POST', $url, $params)->getBody()->getContents(); $resAry = json_decode($resJson, true); return $resAry['access_token'] ?? ''; }
- 投稿日:2020-02-13T13:23:37+09:00
Laravel CSVの出力処理を実装する
概要
Laravelを使って、CSVファイルを出力するサンプルを作成します。
背景
データベースやファイルアクセスをしないテストの方法、インターフェースやジェネレータを使ったコードの書き方等、個別に詳しく書かれた記事はあれど実際使うにはどう書き始めたらいいのかベストプラクティスがわかりませんでした。
日々の業務や副業、勉強会を通じてようやく自分の中で少しずつイメージができてきたので現時点で最高のアウトプットをしていこうと思いました。
目的
この記事ではテストを意識したコードかつ、シンプルに書くことを目標にしてます。
より良いコードにしたいのでアドバイスもらえたらうれしいです。説明不足なところがあったら補足を追記するので気軽に質問等もいただけたら嬉しいです。
環境
- PHP 7.4.1
- Laravel 6.14.0
- MySQL 8.0.19
サンプルコード
https://github.com/ucan-lab/learn-laravel-export-csv
$ git clone git@github.com:ucan-lab/learn-laravel-export-csv.git $ cd learn-laravel-export-csv $ make install $ make app $ php artisan migrate:fresh --seed # csv出力コマンド $ php artisan export:user # テスト実行 $ ./vendor/bin/phpunit今回のゴール
users
テーブルに入ってるデータをcsv出力する処理を作るところまでゴールとします。名前,メールアドレス,作成日,更新日 PROF. RACHELLE KUHIC I,leola.rath@example.com,2020-02-01 23:59:59,2020-02-01 23:59:59 JAYLON WOLF,osinski.fernando@example.net,2020-02-01 23:59:59,2020-02-01 23:59:59 LELAND DECKOW,bokon@example.org,2020-02-01 23:59:59,2020-02-01 23:59:59名前の列は大文字に変換して出力する仕様です。
ベースのコード
環境はこちらのコードを丸コピしてます。
追加したファイル一覧
https://github.com/ucan-lab/learn-laravel-export-csv/pull/1
src/app/Console/Commands/ExportUserCommand.php src/app/Domain/UserRow.php src/app/Domain/UserRowHeader.php src/app/Http/Controllers/Auth/RegisterController.php src/app/Infrastructure/Adapter/DbUserRepository.php src/app/Infrastructure/Adapter/FileUserCsvExport.php src/app/Infrastructure/Adapter/InMemoryUserCsvExport.php src/app/Infrastructure/Adapter/InMemoryUserRepository.php src/app/Infrastructure/Eloquent/User.php src/app/Infrastructure/Port/Export.php src/app/Infrastructure/Port/UserRepository.php src/app/Providers/AppServiceProvider.php src/app/UseCase/UserCsvExportUseCase.php src/database/factories/UserFactory.php src/database/seeds/DatabaseSeeder.php src/database/seeds/UsersTableSeeder.php src/tests/Unit/UserCsvExportUseCaseTest.phpマイグレーション(テーブル)の確認
今回はLaravelが元々用意してくれている users テーブルをそのまま使います。
src/database/migrations/2014_10_12_000000_create_users_table.php<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateUsersTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('users', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('name'); $table->string('email')->unique(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->rememberToken(); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('users'); } }MySQLのテーブル定義も確認しておきます。
$ make mysql mysql> desc users; +-------------------+-----------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------------+-----------------+------+-----+---------+----------------+ | id | bigint unsigned | NO | PRI | NULL | auto_increment | | name | varchar(255) | NO | | NULL | | | email | varchar(255) | NO | UNI | NULL | | | email_verified_at | timestamp | YES | | NULL | | | password | varchar(255) | NO | | NULL | | | remember_token | varchar(100) | YES | | NULL | | | created_at | timestamp | YES | | NULL | | | updated_at | timestamp | YES | | NULL | | +-------------------+-----------------+------+-----+---------+----------------+app/User.php => app/Infrastructure/Eloquent/User.php
LaravelのEloquentモデルはデフォルトだとapp直下に配置されます。
app/Infrastructure/Eloquent/User.php
へ移動します。
依存するファイルも合わせて修正します。詳細はコミットログ参照シーダーの作成
Laravelには、シーディング、モデルファクトリ、Fakerが用意されており、ダミーデータを簡単に作成できます。
$ php artisan make:seeder UsersTableSeeder
src/database/seeds/UsersTableSeeder.php
シーダーのひな形クラスを作ってくれるので下記のように追記します。src/database/seeds/UsersTableSeeder.php<?php declare(strict_types=1); use App\Infrastructure\Eloquent\User; use Illuminate\Database\Seeder; class UsersTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { factory(User::class, 3)->create(); } }上記の用にシーダーを追加するだけで、テストデータを3件作成してくれます。
Userモデルクラスの各プロパティにどんなデータが入るかの定義はモデルファクトリで定義されてます。src/database/factories/UserFactory.php<?php declare(strict_types=1); /** @var \Illuminate\Database\Eloquent\Factory $factory */ use App\Infrastructure\Eloquent\User; use Faker\Generator as Faker; use Illuminate\Support\Str; /* |-------------------------------------------------------------------------- | Model Factories |-------------------------------------------------------------------------- | | This directory should contain each of the model factory definitions for | your application. Factories provide a convenient way to generate new | model instances for testing / seeding your application's database. | */ $factory->define(User::class, function (Faker $faker) { return [ 'name' => $faker->name, 'email' => $faker->unique()->safeEmail, 'email_verified_at' => now(), 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password 'remember_token' => Str::random(10), ]; });元々用意されている
src/database/seeds/DatabaseSeeder.php
でUsersTableSeeder
を呼び出す記述を追記します。src/database/seeds/DatabaseSeeder.php<?php declare(strict_types=1); use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder { /** * Seed the application's database. * * @return void */ public function run() { $this->call(UsersTableSeeder::class); } }UserRow, UserRowHeader ドメインを定義
この辺りから本題です。
App\Domain\UserRowHeader
app/Domain/UserRowHeader.php<?php declare(strict_types=1); namespace App\Domain; final class UserRowHeader { private const EOF = "\n"; private const HEADER = [ '名前', 'メールアドレス', '作成日', '更新日', ]; public static function toCsv(): string { return implode(',', self::HEADER) . self::EOF; } }UserRowHeader ドメインクラスではCSVのヘッダー行となる1行目の定義をしてます。
App\Domain\UserRow
app/Domain/UserRow.php<?php declare(strict_types=1); namespace App\Domain; use Carbon\Carbon; final class UserRow { private const EOF = "\n"; private const DATE_FORMAT = 'Y-m-d H:i:s'; private string $name; private string $email; private Carbon $createdAt; private Carbon $updatedAt; public function __construct( string $name, string $email, Carbon $createdAt, Carbon $updatedAt ) { $this->name = $name; $this->email = $email; $this->createdAt = $createdAt; $this->updatedAt = $updatedAt; } /** * @return string */ public function toCsv(): string { return implode(',', $this->toArray()) . self::EOF; } /** * @return array */ private function toArray(): array { return [ $this->getName(), $this->email, $this->createdAt->format(self::DATE_FORMAT), $this->updatedAt->format(self::DATE_FORMAT), ]; } /** * @return string */ private function getName(): string { return strtoupper($this->name); } }UserRow ドメインクラスではCSVの1行の定義をしてます。
ユーザー名は大文字や日付のフォーマット等の業務ロジックはここにまとめます。UserCsvExportUseCase を定義
app/Infrastructure/Export.php<?php declare(strict_types=1); namespace App\Infrastructure\Port; interface Export { public function prepare(string $header): void; public function write(string $row): void; public function disorganize(): void; }Export インターフェースを継承するクラスは prepare(前処理)、write(書き込み)、disorganize(後処理)のメソッドを契約します。
app/Infrastructure/UserRepository.php<?php declare(strict_types=1); namespace App\Infrastructure\Port; use Generator; interface UserRepository { public function findAll(): Generator; }UserRepository インターフェースを継承するクラスはfindAll(全件取得)のメソッドを契約します。
app/UseCase/UserCsvExportUseCase.php<?php declare(strict_types=1); namespace App\UseCase; use App\Domain\UserRow; use App\Domain\UserRowHeader; use App\Infrastructure\Port\Export; use App\Infrastructure\Port\UserRepository; final class UserCsvExportUseCase { /** * @var UserRepository */ private UserRepository $repository; /** * @var Export */ private Export $export; /** * @param UserRepository $repository * @param Export $export */ public function __construct(UserRepository $repository, Export $export) { $this->repository = $repository; $this->export = $export; } /** * @return void */ public function handle(): void { $this->export->prepare(UserRowHeader::toCsv()); /** @var UserRow $row */ foreach ($this->repository->findAll() as $row) { $this->export->write($row->toCsv()); } $this->export->disorganize(); } }UserCsvExportUseCase ユースケースクラスはUserRepositoryとExportインターフェースに依存します。
前処理して、全件取得して、書き込みして、後処理して終わるシンプルな作りにできました。
各インターフェースを契約するクラスの中身はあとで書きます。ユースケースのテストを書く
テストを書く際、データベースやファイルに直接読み書きするようなテストを書いてしまうと
最初は問題ないですが、テストが増えるにつれてテストの実行速度がどんどん落ちてしまいます。そのため、テストを書く際はデータベースやファイルアクセスが発生しないようメモリ内で良い感じのテストを書きます。
InMemoryUserCsvExport
app/Infrastructure/Adapter/InMemoryUserCsvExport.php<?php declare(strict_types=1); namespace App\Infrastructure\Adapter; use App\Infrastructure\Port\Export; final class InMemoryUserCsvExport implements Export { /** * @var string */ public string $file; /** * @param string $header */ public function prepare(string $header): void { $this->file = $header; } /** * @param string $row */ public function write(string $row): void { $this->file .= $row; } /** * @return void */ public function disorganize(): void { } }
Export
インターフェースを契約したInMemoryUserCsvExport
クラスを実装します。
やってることは簡単で、prepare
メソッドで$file
プロパティに文字列を入れてwrite
メソッドが呼ばれたらどんどん追記する形です。
実際にファイルアクセスする場合はdisorganize
でfclose
等の処理を入れますが、ファイルアクセスしないので関数だけ定義してます。InMemoryUserRepository
app/Infrastructure/Adapter/InMemoryUserRepository.php<?php declare(strict_types=1); namespace App\Infrastructure\Adapter; use App\Domain\UserRow; use App\Infrastructure\Eloquent\User; use App\Infrastructure\Port\UserRepository; use Generator; final class InMemoryUserRepository implements UserRepository { private array $usersAttributes; /** * @param array $users */ public function __construct(array $users) { $this->usersAttributes = $users; } /** * @return Generator */ public function findAll(): Generator { foreach ($this->usersAttributes as $userAttributes) { yield $this->makeUserRow(factory(User::class)->make($userAttributes)); } } /** * @param User $user * @return UserRow */ private function makeUserRow(User $user): UserRow { return new UserRow( $user->name, $user->email, $user->created_at, $user->updated_at ); } }補足: ジェネレータ
findAll
の戻り値の型としてGenerator
オブジェクトを返すと呼び出した側はforeach
を使って順に呼び出すことができます。
return
ではなくyield
を指定します。
yield
はUserRow
のインスタンスを返してます。// UserCsvExportUseCase で findAll を foreach でループ処理できます。 foreach ($this->repository->findAll() as $row) { $this->export->write($row->toCsv()); }ジェネレータのメリットは
foreach
でループ処理するために巨大な配列を持つ必要がなく1件処理が終わったらメモリを解放して次の処理を実行してくれるので、バッチ処理等のメモリをたくさん使いそうな場合に効果を発揮します。UserCsvExportUseCaseTest
先ほど作成した
InMemoryUserRepository
とInMemoryUserCsvExport
を使ってテストコードを書きます。tests/Unit/UserCsvExportUseCaseTest.php<?php declare(strict_types=1); namespace Tests\Unit; use App\Infrastructure\Adapter\InMemoryUserCsvExport; use App\Infrastructure\Adapter\InMemoryUserRepository; use App\UseCase\UserCsvExportUseCase; use Tests\TestCase; final class UserCsvExportUseCaseTest extends TestCase { /** * @param array $users * @param string $expectedCsv * @dataProvider dataResolve */ public function testResolve(array $users, string $expectedCsv): void { $repository = new InMemoryUserRepository($users); $export = new InMemoryUserCsvExport(); $useCase = new UserCsvExportUseCase($repository, $export); $useCase->handle(); $this->assertEquals($expectedCsv, $export->file); } /** * @return array */ public function dataResolve(): array { return [ '正常3件' => $this->case正常3件(), '正常0件' => $this->case正常0件(), ]; } /** * @return array */ public function case正常3件(): array { $usersAttributes = [ ['name' => 'yamada', 'email' => 'yamada@example.com', 'created_at' => '2020-01-01 00:00:00', 'updated_at' => '2020-01-01 00:00:00'], ['name' => 'suzuki', 'email' => 'suzuki@example.com', 'created_at' => '2020-01-01 00:00:00', 'updated_at' => '2020-01-01 00:00:00'], ['name' => 'tanaka', 'email' => 'tanaka@example.com', 'created_at' => '2020-01-01 00:00:00', 'updated_at' => '2020-01-01 00:00:00'], ]; $expectedCsv = <<< EOT 名前,メールアドレス,作成日,更新日 YAMADA,yamada@example.com,2020-01-01 00:00:00,2020-01-01 00:00:00 SUZUKI,suzuki@example.com,2020-01-01 00:00:00,2020-01-01 00:00:00 TANAKA,tanaka@example.com,2020-01-01 00:00:00,2020-01-01 00:00:00 EOT; return [ $usersAttributes, $expectedCsv, ]; } /** * @return array */ public function case正常0件(): array { $usersAttributes = []; $expectedCsv = <<< EOT 名前,メールアドレス,作成日,更新日 EOT; return [ $usersAttributes, $expectedCsv, ]; } }想定している
$expectedCsv
の値とユースケースを実行して作成された値$export->file
が一致すればokです。補足: dataProvider
PHPUnitのdataProviderについて補足です。
PHPUnitを実行する際に--debug
オプションを付けると詳細ログが見れます。
dataProvider
で作った引数もログに出てくるので分かりやすくなります。データプロバイダ | phpunit.readthedocs.io
$ ./vendor/bin/phpunit --debug PHPUnit 8.5.2 by Sebastian Bergmann and contributors. Test 'Tests\Unit\ExampleTest::testBasicTest' started Test 'Tests\Unit\ExampleTest::testBasicTest' ended Test 'Tests\Unit\UserCsvExportUseCaseTest::testResolve with data set "正常3件" (array(array('yamada', 'yamada@example.com', '2020-01-01 00:00:00', '2020-01-01 00:00:00'), array('suzuki', 'suzuki@example.com', '2020-01-01 00:00:00', '2020-01-01 00:00:00'), array('tanaka', 'tanaka@example.com', '2020-01-01 00:00:00', '2020-01-01 00:00:00')), '名前,メールアドレス,作成日,更新日\nYAMADA,ya...0:00\n')' started Test 'Tests\Unit\UserCsvExportUseCaseTest::testResolve with data set "正常3件" (array(array('yamada', 'yamada@example.com', '2020-01-01 00:00:00', '2020-01-01 00:00:00'), array('suzuki', 'suzuki@example.com', '2020-01-01 00:00:00', '2020-01-01 00:00:00'), array('tanaka', 'tanaka@example.com', '2020-01-01 00:00:00', '2020-01-01 00:00:00')), '名前,メールアドレス,作成日,更新日\nYAMADA,ya...0:00\n')' ended Test 'Tests\Unit\UserCsvExportUseCaseTest::testResolve with data set "正常0件" (array(), '名前,メールアドレス,作成日,更新日\n')' started Test 'Tests\Unit\UserCsvExportUseCaseTest::testResolve with data set "正常0件" (array(), '名前,メールアドレス,作成日,更新日\n')' ended Test 'Tests\Feature\ExampleTest::testBasicTest' started Test 'Tests\Feature\ExampleTest::testBasicTest' ended Time: 3.04 seconds, Memory: 20.00 MB OK (4 tests, 4 assertions)CSVの出力処理を実装する
テストコードが書けたところで、実際にデータベースから取得してCSVファイルを出力する処理を実装します。
DbUserRepository
app/Infrastructure/Adapter/DbUserRepository.php<?php declare(strict_types=1); namespace App\Infrastructure\Adapter; use App\Domain\UserRow; use App\Infrastructure\Eloquent\User; use App\Infrastructure\Port\UserRepository; use Generator; final class DbUserRepository implements UserRepository { /** * @return Generator */ public function findAll(): Generator { /** @var User $user */ foreach (User::query()->cursor() as $user) { yield new UserRow( $user->name, $user->email, $user->created_at, $user->updated_at ); } } }UserRepositoryを契約したDbUserRepositoryクラスです。
補足: User::query()->cursor()
cursor()
を使うとPDOStatement::fetch
結果セットから1行ずつ取得できます。cursor()
の返り値もジェネレータオブジェクトになります。
User::query()->cursor()
ではなくUser::all()
も動作するかと思いますが、一度に大量のデータを取得するのでデータ件数によってはメモリオーバーになってしまう懸念があります。FileUserCsvExport
app/Infrastructure/Adapter/FileUserCsvExport.php<?php declare(strict_types=1); namespace App\Infrastructure\Adapter; use App\Infrastructure\Port\Export; final class FileUserCsvExport implements Export { /** * @var string */ private string $streamFilePath; /** * @var resource */ private $handle; /** * @param string $header * @return void */ public function prepare(string $header): void { $this->streamFilePath = $this->makeStreamFile(); $this->handle = fopen($this->streamFilePath, 'wb+'); $this->write($header); } /** * @param string $row * @return void */ public function write(string $row): void { fwrite($this->handle, $row); } /** * @return void */ public function disorganize(): void { fclose($this->handle); // 後処理 配置したい場所へコピーする等 dump(file_get_contents($this->streamFilePath)); unlink($this->streamFilePath); } /** * @return string */ private function makeStreamFile(): string { return tempnam(sys_get_temp_dir(), config('app.name')); } }Exportを契約したFileUserCsvExportクラスです。
ExportUserCommand
app/Console/Commands/ExportUserCommand.php<?php declare(strict_types=1); namespace App\Console\Commands; use App\UseCase\UserCsvExportUseCase; use Illuminate\Console\Command; class ExportUserCommand extends Command { /** * The name and signature of the console command. * * @var string */ protected $signature = 'export:user'; /** * The console command description. * * @var string */ protected $description = 'export user data.'; /** * @var UserCsvExportUseCase */ private UserCsvExportUseCase $useCase; /** * ExportUserCommand constructor. * @param UserCsvExportUseCase $useCase */ public function __construct(UserCsvExportUseCase $useCase) { parent::__construct(); $this->useCase = $useCase; } /** * @return void */ public function handle(): void { $this->useCase->handle(); } }LaravelにはArtisanコンソールというコマンドラインインターフェイスが用意されてます。
コマンドクラスを作るだけで簡単に自作コマンドを追加できます。ExportUserCommandでやってることは、UserCsvExportUseCaseのインスタンスを受け取って、handleメソッドを呼び出すだけです。
$ php artisan export:user上記のコマンドが追加されます。
依存性の注入(DI)
app/Providers/AppServiceProvider.php<?php declare(strict_types=1); namespace App\Providers; use App\Infrastructure\Adapter\DbUserRepository; use App\Infrastructure\Adapter\FileUserCsvExport; use App\UseCase\UserCsvExportUseCase; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * Register any application services. * * @return void */ public function register() { $this->app->bind(UserCsvExportUseCase::class, function ($app) { return new UserCsvExportUseCase(new DbUserRepository(), new FileUserCsvExport()); }); } /** * Bootstrap any application services. * * @return void */ public function boot() { // } }UserCsvExportUseCaseクラスをサービスコンテナに登録します。
ここで登録しているので、ExportUserCommandはコンストラクタインジェクションでUserCsvExportUseCaseクラスのインスタンスを受け取れます。
- 投稿日:2020-02-13T11:27:06+09:00
selenium php-webdriver update
$composer update Loading composer repositories with package information Updating dependencies (including require-dev) Nothing to install or update Package facebook/webdriver is abandoned, you should avoid using it. Use php-webdriver/webdriver instead. Generating autoload filesversion 1.8.0 から名前が facebook/php-webdriver から php-webdriver/webdriverに変わりました
jsonを書き換えますcomposer.json"require": { - "facebook/webdriver": "^1.7.1", + "php-webdriver/webdriver": "^1.8.0", }アップデートすれば新しいバージョンが入ります
$ composer update Loading composer repositories with package information Updating dependencies (including require-dev) Package operations: 2 installs, 1 update, 1 removal - Removing facebook/webdriver (1.7.1) - Updating symfony/process (v4.4.4 => v5.0.4): Downloading (100%) - Installing symfony/polyfill-mbstring (v1.13.1): Downloading (100%) - Installing php-webdriver/webdriver (1.8.0): Downloading (100%) php-webdriver/webdriver suggests installing ext-SimpleXML (For Firefox profile creation) Writing lock file Generating autoload filesgithub php-webdriver
https://github.com/php-webdriver/php-webdriver
- 投稿日:2020-02-13T10:30:11+09:00
?【CakePHP2】Error: Call to a member function dispatchMethod() on nullは環境設定で修正
環境
PHP 7.2.21
CakePHP 2.10.18やりたいこと
下記のエラーを修正
Error: Call to a member function dispatchMethod() on null #0 (Modelのファイルパス)(223): Model->__call('makeRoleTargetJ...', Array) #1 [internal function]: (対象のModel名)->__construct(Array) #2 /(CakePHPディレクトリ)/Vendor/cakephp/cakephp/lib/Cake/Utility/ClassRegistry.php(169): ReflectionClass->newInstance(Array) #3 /(CakePHPディレクトリ)/Vendor/cakephp/cakephp/lib/Cake/Console/Shell.php(316): ClassRegistry::init('(対象のModel名)') #4 /(CakePHPディレクトリ)/Vendor/cakephp/cakephp/lib/Cake/Console/Shell.php(269): Shell->loadModel('(対象のModel名)') #5 /(CakePHPディレクトリ)/Vendor/cakephp/cakephp/lib/Cake/Console/Shell.php(228): Shell->_loadModels() #6 /(CakePHPディレクトリ)/Vendor/cakephp/cakephp/lib/Cake/Console/ShellDispatcher.php(221): Shell->initialize() #7 /(CakePHPディレクトリ)/Vendor/cakephp/cakephp/lib/Cake/Console/ShellDispatcher.php(66): ShellDispatcher->dispatch() #8 /(CakePHPディレクトリ)/(Projectディレクトリ)/Console/cake.php(48): ShellDispatcher::run(Array) #9 {main}やったこと
env.phpの環境変数を確認して修正する
ModelやControllerは悪くなかった
- 投稿日:2020-02-13T07:07:58+09:00
PHP オブジェクト指向 勉強用
勉強用メモです。
クラスがもつ変数をプロパティ
クラスがもつ関数をメソッド
- 投稿日:2020-02-13T04:10:12+09:00
構造化データの読み込みにまつわる問題 〜evalインジェクション〜
XMLやJSONなどの構造を持つデータを保存や送受信できるように変換することをシリアライズと呼ぶ。
シリアライズは配列データを配列データとして保存できたりなど、配列の要素や構造が動的に変化するデータを扱う際などに使用される。
ここではシリアライズに関する脆弱性の一つであるevalインジェクションについて記載する。概要
プログラム言語にはevalと呼ばれる関数・機能があり、これは文字列(各言語のコード)を式として評価したのものである。
evalは使い方によって外部から送り込んだスクリプトが実行されることがある。
それをevalインジェクション攻撃と言い、そのような攻撃を受ける脆弱性をevalインジェクション脆弱性と呼ぶ。evalの例
JavaScript(MDN)
eval(string)
PHP(php.net)
eval ( string $code ) : mixed
evalインジェクションによる影響は以下のOSコマンド・インジェクション攻撃と同じ。
- 情報漏洩
- サイト改ざん
- 不正な機能実行
- 他サイトへの攻撃(踏み台)
- 暗号通貨の採掘(マイニング)
evalインジェクション脆弱性の対策は以下の通り
- evalに相当する機能を使わない
- evalの引数には外部からのパラメータを含めない
- evalに与える外部からのパラメータを英数字に限定する
攻撃手法と影響
シリアライズしてフォーム間を受け渡す場合の脆弱性について。
例としてPHPのvar_export関数を使ってシリアライズした後にbase64にエンコードしデータを受け渡す。受け渡されたデータのbase64をデコードし、evalを用いて元のデータに戻す(デシリアライズする)とする。
参考イメージ4e-001.php<?php $a = array(1, 2, 3); $ex = var_export($a, true); $b64 = base64_encode($ex); ?> <body> <form action="4e-002.php" method="GET"> <input type="hidden" name="data" value="<?php echo htmlspecialchars($b64) ?>"> <input type="submit" value="次へ"> </form> </body>4e-002.php<?php $data = $_GET['data']; $str = base64_decode($data); eval('$a = ' . $str . ';'); ?> <body> <?php var_dump($a); ?> </body>攻撃手法
evalに渡す式に任意の文を追加(スクリプトを注入)する。
$a = 式: 任意の文;
$a = 0: phpinfo();
OWASP ZAPで上記を試すとphpinfoが表示され、外部から注入したスクリプトが実行された状態となる。
これらの事例から情報漏洩やサイト改ざんなどの影響を受ける可能性がある。脆弱性が生まれる原因
evalに与えるパラメータをチェックしていないため。
PHPであれば任意のコードを実行できてしまうようにevalはそもそも危険な言語構造であること。evalインジェクション脆弱性の原因は以下の通り。
- evalを用いることがそもそも危険
- evalに与えるパラメータのチェックがされていない
対策
evalインジェクション脆弱性の対策は以下の通り。
- eval(同等機能含む)を使わない
- evalの引数に外部からのパラメータを指定しない
- evalの与える外部からのパラメータを英数字に制限する
evalを使わない
eval(同等機能含む)を使わず、eval以外の選択肢を検討する。
シリアライズが目的であれば以下の選択肢がある。シリアライズ以外の目的でもevalは極力使わない実装を検討する。
evalを使わずに同等の処理は実装可能。evalの引数に外部からのパラメータを指定しな
evalを使った場合でも外部からパラメータを指定しない。
外部からスクリプトを注入できなくすれば安全だが、注入経路がファイルやデータベースの場合この対策は使えない。evalに与える外部からのパラメータを英数字に制限する
外部から与えるパラメータを英数字に限定できればスクリプト注入に必要な記号文字を使えなくすることができる。
正規表現による入力値検証などを行う。まとめ
evalを使わない実装を行う。
どうしてもevalを使わなければならない場合は外部からのスクリプト注入を防ぐ対策を行う。さらに進んだ学習のために
- 投稿日:2020-02-13T02:01:23+09:00
laravel migration カラム名変更(初心者向け)
phpについての初投稿になります!
リファレンスにも書いてありますが、つい忘れてしまうのでメモ程度に紹介します
laravel 6.x系です
osはmacですカラム名を変更、削除するには
ターミナルで
$ composer require doctrine/dbal↑のコマンドを叩き、composerで"doctrine/dbal"
をインストールしましょう!(リファレンスに書いてあるので臆せずインストールしましょう!)
laravelはデフォルトではカラム名の変更や削除はできません!(なんでやねん!まぁなんか理由があるのでしょう)
で、ターミナルで
$ php artisan make:migration rename_変更前のカラム名_to_変更後のカラム名_on_テーブル名_table --table=テーブル名とコマンドを叩きます(オプションの"--table=テーブル名"により、migtarionファイル作成時にテーブル名が追加され作成されます。また、migrationファイルの名前は正直適当でいいと思いますが、ファイル名をみて何の内容のmigrationファイルが実行されたかわかりやすい名前をつけた方が後々楽になると思います。)
〇〇_rename_変更前のカラム名_to_変更後のカラム名_on_テーブル名_table.php<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class RenameNightSpandingToNightSpendingOnCostsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('costs', function (Blueprint $table) { $table->renameColumn('night_spanding', 'night_spending');//<-記述 }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('costs', function (Blueprint $table) { $table->renameColumn('night_spending', 'night_spanding');//<-記述 }); } }こんな感じでマイグレーションファイルを作成します
upは
$ php artisan migrate↑で実行される内容
downは
$ php artisan migrate:rollback↑などで実行される内容を記述します
なので、カラム名の変更や、型の変更などは、基本的にはupで書いた内容の逆を記述すればいいと思います。
(php artisan migrate:refreshなども全てロールバックしてmigrateし直すコマンドなどあるので、downはちゃんと書いておかないとエラーの原因になります!)で、
$ php artisan migrateでカラム名を変更することができます!
カラム削除する場合でも、"doctrine/dbal"のインストールが必要みたいですので、個人的にはlaravelプロジェクトを立ち上げたらすぐにインストールしておいた方がいいのかな?と感じてます。
- 投稿日:2020-02-13T00:14:56+09:00
PHP クラスの定義とインスタンスの作成
目的
- クラスの定義方法とインスタンスの作成方法を記載する
用語
- クラス内で定義される変数: プロパティ
- クラス内で定義される関数: メソッド
クラスの定義
- 下記にUserクラスを定義する。
- nameプロパティを定義する。
- Hi, i am プロパティとなるようなsayHiメソッドを定義する。
class User { // プロパティの宣言 public $name; //constructの宣言 public function __construct($name) { $this->name = $name; } // メソッドの宣言 public function sayHi() { echo "Hi, i am $this->name"; } }インスタンスの作成
- 先に定義したクラスの下で下記の行を記載する。
$miriwo = new User("miriwo");インスタンスのメソッドを使用する
- 下記にUserクラスを元に作成したmiriwoインスタンスを使用してsayHiメソッドを実行する。(>の後ろは出力)
$miriwo->sayHi(); >Hi, i am miriwo