React Hook Form(Zodで型推論)Notionクローンアプリの認証、フォーム送信で実装

React Hook Form は、Reactでフォームを扱うためのライブラリです

https://react-hook-form.com

フォームの入力内容はuseStateで管理しない

通常フォームの入力内はuseStateで入力内容変更のたびに再レンダリングされて、いつでもアクセス可能な値として管理してます。

React Hook Formは入力時に再レンダリングせず、送信時にrefでDOMから値を取得します

React Hook Formは非制御コンポーネントからどうやって変更を検知しているのか – commmune Engineer Blog

(途中で値を取得したい場合は、そのように記述することも可能)

主要な機能

register、handleSubmit,、formStateこの3つはreact-hook-formで最も基本的で頻繁に使用される機能です。

register

inputやtextareaなどのフォーム要素をreact-hook-formに「登録」して、値の管理バリデーションを任せる機能

handleSubmit

フォームで下記のように書くとonSubmit()をコールする前にバリデーションをして、OKであれば実行します

<form onSubmit={handleSubmit(onSubmit)}>

onSubmit={…} ← これはHTML属性
onSubmit ← これは自分で定義した関数名

名前が同じで紛らわしいですが、たまたま慣習的に同じ名前を使っているだけです。

handleSubmit(onSubmit)

  1. フォーム送信イベントをキャッチ
  2. event.preventDefault() を実行(ページリロード防止)
  3. 全フィールドのバリデーション実行
  4. エラーがあれば → onSubmitは呼ばれない
  5. エラーがなければ → onSubmit(data) を実行

formState

フォームの現在の状態(エラー、送信中、タッチ済みなど)を取得できるオブジェクトです。

よく使うプロパティ

typescript

const { 
  errors,        // バリデーションエラー
  isValid,       // 全フィールドが有効か
  isSubmitting,  // 送信処理中か
  isDirty,       // 初期値から変更されたか
  isSubmitted,   // 送信が完了したか
  touchedFields  // フォーカスされたフィールド
} = formState;

バリデーション機能

Zodを使用したバリデーション

Zodとはデータのバリデーションと型定義を同時に行えるライブラリ です。

Zodの基本的な使用方法

z.object() でスキーマを作り、parse() でデータを検証します。
エラーがある場合は例外を投げ、正しい場合のみ安全に型付きデータとして返します。

またz.infer で Zodのスキーマ型推論もできます

型推論とは、プログラマが型を明示的に書かなくても、コンパイラが自動的に型を判定してくれる機能

↓エディターでホバーすると型の確認できます(これは型推論ではなく普通に明示的にFCと型指定されてます)

zodResolverでReact hook formとZodを連携

React Hook Form がフォーム送信時に呼び出す「バリデーション処理」を、
Zod のスキーマを使って実行できるようにする関数 です。

React Hook Formのみ(register内でバリデーション定義)

export function RHFOnlyForm() {
  const { register, handleSubmit, formState: { errors } } = useForm();

  const onSubmit = (data) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        {...register("email", {
          required: "メールアドレスは必須です",
          pattern: {
            value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
            message: "有効なメールアドレスを入力してください",
          },
        })}
      />
      {errors.email && <p>{errors.email.message}</p>}
      <button type="submit">送信</button>
    </form>
  );
}

React Hook Form + Zod

const schema = z.object({
  email: z.string().nonempty("メールアドレスは必須です").email("有効なメールアドレスを入力してください"),
});

export function RHFZodForm() {
  const { register, handleSubmit, formState: { errors } } = useForm({
    resolver: zodResolver(schema),
  });

  const onSubmit = (data) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("email")} />
      {errors.email && <p>{errors.email.message}</p>}
      <button type="submit">送信</button>
    </form>
  );
}

「Zod」を使用したスキーマの例
https://ics.media/entry/240611

【JavaScript】非同期処理をコールバック関数をしようせずにPromiseで実装できる理由

非同期処理とは

同期処理か非同期処理かは関数(やメソッド)に対して使う言葉です

同期処理は、ある処理が完了するまで次の処理に進まない動作を指します。
一方、非同期処理は、処理の完了を待たずに他の処理を並行して進めることができ、メインスレッドが待機状態になることはありません。

一般的にはこのように説明されますが、より正確には「その関数が非同期APIであるかどうか」が本質的な違いです。

非同期APIとは

非同期APIとは、ブラウザやNode.jsといったJavaScriptランタイムが提供する、非同期的に結果を返す仕組みをもつ関数のことです。

例)たとえば setTimeout や XMLHttpRequest、addEventListener

JavaScriptで非同期処理を実現する手段は、基本的に「非同期API」を利用することだけです。

例)非同期処理

setTimeout(() => {
  console.log("非同期処理完了");
}, 1000);

console.log("先に実行");

// 出力結果
先に実行
非同期処理完了

なお、setTimeoutの引数を0にして、0ミリ秒後としても出力順番は同じです(先に実行→非同期処理完了)

理由はsetTimeoutで登録された内容はタスクキューにスタックされて、メインスレッドの全ての同期処理が完了後にコールされるからです

例)コールバック関数を使用した非同期処理

const fs = require('fs');

/**
 * readFile
 * @param {string} filename - 読み込むファイルのパス
 * @param {function} callback - 読み込み完了後に実行されるコールバック関数
 *   @param {?Error} callback.err - Node.js が内部で自動的に渡すエラーオブジェクト(発生しない場合は null)
 *   @param {string|Buffer} callback.data - Node.js が内部で自動的に渡す読み込み結果
 * @returns {void}
 * @description
 * Node.js の組み込み非同期API。ファイルの読み込みが完了すると、
 * Node.js ランタイムが内部的に callback(err, data) を呼び出す。
 */
fs.readFile("./input01.txt", 'utf8', (err, data) => {
    if (!err) console.log("1");
});

fs.readFile("./input02.txt", 'utf8', (err, data) => {
    if (!err) console.log("2");
});

fs.readFile("./input03.txt", 'utf8', (err, data) => {
    if (!err) console.log("3");
});

fs.readFile("./input04.txt", 'utf8', (err, data) => {
    if (!err) console.log("4");
});

fs.readFile("./input05.txt", 'utf8', (err, data) => {
    if (!err) console.log("5");
});


// 出力結果は

Promise

Promiseオブジェクトは、.then(), .catch(), .finally()のメソッドを持ちます。

  • “pending” 処理中
  • “fulfilled” 成功
  • “rejected” 失敗

上記の状態や結果は内部スロットとして隠されています。

Web制作受注後の進め方(ヒアリング→デザイン→コーディング)

ヒアリング

この度はサイト制作のご依頼ありがとうございます。
デザインや構成を進めるために、下記についてお伺いできればと思います。

特に決まっていない部分は「特になし」や「相談したい」と書いていただければ大丈夫です。

ヒアリング内容回答例
納期(納品日、公開日)
依頼背景
ページ数5枚のページ
必要なページは何でしょうか?トップページ、会社概要、地図、お問い合わせ、お知らせ
公開後に更新作業をされる予定はありますか?


その他ご要望がございましたらご記入ください

デザイン

情報設計→ワイヤー→カンプ

デザインの前に情報設計

サイトマップ、主要導線、各ページの目的とコンテンツの粒度を整理

ヒアリング内容回答例
納期(納品日、公開日)
メインカラー、サブカラー、アクセントカラーブラウン・ホワイト
フォント指定ゴシック系
ご用意いただけるものロゴ・テキスト・画像

コーディング

ページ制作は“上から順に塗る”のではなく、共通レイアウト→グローバルナビ→ボタン・フォーム・カード等のコンポーネント→テンプレート→個別ページの順

WordPress案件

よくある事例

ブログ・サービス事例の更新機能についても実装可能です。
ブログは標準機能で対応可能ですが、施工事例については
別途カスタマイズが必要となり、概算で○○円前後の追加費用を想定しております。

もしサービス事例を頻繁に更新されない場合は、
管理画面を設けず「都度こちらで反映する(固定掲載)」形式も可能です。
この場合は追加費用を抑えられますので、運用方針に合わせてご提案させていただきます。

【WordPress】WPGraphQLプラグイン

WordPressをヘッドレスCMSとして活用すると、使い慣れた管理画面でコンテンツを管理しながら、フロントエンドは任意の技術で自由に構築できます。

この構成で特に便利なのがWPGraphQLプラグインです。このプラグインを使えば、REST APIよりも効率的にデータを取得でき、必要な情報だけを柔軟に取得できます。WPGraphQLプラグイン
https://www.wpgraphql.com

GraphiQL IDE

GraphiQL IDEはGraphQLクエリを管理画面上でテスト実行できる機能です。

  • 左側の「Query Composer」に取得できるデータの一覧が並んでいます
  • 中央が「クエリ入力欄」です「Query Composer」にチェックを入れると自動で追加されます
  • 右側がWordPressから返ってきたデータで、クエリ入力欄のうえの「▶」ボタンを押すと出力されます

例)

投稿一覧を取得(タイトル+スラッグ)

query {
  posts(first: 5) {
    nodes {
      title
      slug
    }
  }
}

↓結果

{
  "data": {
    "posts": {
      "nodes": [
        { "title": "お知らせ1", "slug": "news1" },
        { "title": "お知らせ2", "slug": "news2" }
      ]
    }
  }
}

エンドポイント

WPGraphプラグインをインストールすると自動でエンドポイントが生成されます

http://あなたのサイトのドメイン/graphql

RESTとGraphQLの違い

項目RESTGraphQL
エンドポイント複数(リソースごと)単一(/graphql
データ取得サーバーが構造を決定クライアントが必要な項目を指定
HTTPメソッドGET, POST, PUT, DELETE主にPOST
過不足過剰/不足が起きやすい必要なデータだけ取得
複数リソース取得複数回リクエスト必要1回で取得可能
学習コスト低い高い
キャッシュHTTPキャッシュが使える工夫が必要

REST

GET /users/123        → ユーザー情報(全項目)
GET /users/123/posts  → 投稿一覧(全項目)

GraphQL

POST /graphql
{ user(id: 123) { name, posts { title } } }
→ 必要な項目だけ1回で取得

WordPressとNext.jsでヘッドレスCMSを開発する場合

generateStaticParams()でページ生成

https://nextjsjp.org/docs/app/api-reference/functions/generate-static-params

個人アカウントのリポジトリをGithub Organizationに移管(Amplify )

現在の状況としては、対象のリポジトリがAmplify Gen2に連携してデプロイ済みなのですが、そのリポジトリが個人アカウントで管理されています。

やりたいこととしては、これを企業アカウントのリポジトリとして管理できるように移行したいと考えています。

GitHub リポジトリの引き継ぎ方法の選択肢

  • Transfer Repository
  • コピー git push –mirror
  • フォーク

いずれもの方法でリポジトリを引き継いだとしても、Amplify側で新リポジトリに接続しなおす必要があります

再連携をしない限り移行先のリポジトリからデプロイ(更新の反映)はできません、すでに公開されているアプリはそのまま動き続ける

コミット履歴個人アカウント側
Transfer Repository全て残るリポジトリは消える
移管先の Organization に自動的に コラボレータ(権限付きメンバー)として追加 される?
コピー git push –mirror全て残る
フォークフォーク元関連が残る?

Transfer Repository

リポジトリをTransfer

一時的に旧リポジトリは新リポジトリにリダイレクト

そのためAmplify 側で再ビルドした場合、
一時的に新リポジトリを連携していなくてもビルドされる?

新リポジトリで修正し、その内容をもとにプッシュ、ビルドする前にAmplify に連携が必要

GitHub Docs リポジトリを移譲する
https://docs.github.com/ja/repositories/creating-and-managing-repositories/transferring-a-repository

コピー git push –mirror

–mirrorオプションはbare repository(ベアリポジトリ)を使用します

bare repositoryとは通常とは異なり、作業ツリーがないリポジトリ

myproject/
  ├── .git/         ← Gitの管理情報
  ├── src/          ← 実際のソースコード
  ├── index.html
  └── ...
myproject.git/      ← 作業ツリーなし
  ├── HEAD
  ├── objects/
  ├── refs/
  └── config

なぜbare repositoryをバックアップで使用すべき理由は以下の通りです

  • 通常クローンは「開発用に便利な形」に変換される
  • bare クローンは「元と全く同じ形」でコピーされる
  • バックアップは bare を選べば安全

リポジトリのバックアップ作業

GitHubリポジトリの移行手順と注意点まとめ(2025年版)
https://zenn.dev/arsaga/articles/2b654ebdf82571?utm_source=chatgpt.com#%E3%83%90%E3%83%83%E3%82%AF%E3%82%A2%E3%83%83%E3%83%97%E4%BD%9C%E6%A5%AD

手順

# SSHでリポジトリを完全コピー
git clone --mirror git@github.com:USER/REPO.git

GitHub 上で新規リポジトリを作成 REPO-archive

# archiveという名前のリモートを追加
git remote add archive git@github.com:USER/REPO-archive.git


git remote -v

origin   git@github.com:USER/REPO.git (fetch)
origin   git@github.com:USER/REPO.git (push)
archive  git@github.com:USER/REPO-archive.git (fetch)
archive  git@github.com:USER/REPO-archive.git (push)
# archiveリモートににプッシュ
git push --mirror archive

Organization 管理者の承認

メンバーにリポジトリ移管の受け入れが許可されているか

移管の受け入れ(Transfer の承認)をメンバーに許可しているか?

外部サービスとの連携をしているリポジトリの場合

GitHub Organization 配下のリポジトリは、個人アカウントのリポジトリと違い、
アプリや外部サービスがアクセスする際に Organization 管理者の承認 が必要

Amplify との連携の許可の場合

GitHub 組織設定にてAmplify(GitHub App または OAuth App)の承認がOrganization管理者にて要対応
※AmplifyのGitリポジトリからデプロイする機能使用するため

AWS CLIとは

AWS コマンドラインインターフェイスとは何ですか?
https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html

GitHub Appが原因でエラー

GitHub App の使用について
https://docs.github.com/ja/apps/using-github-apps/about-using-github-apps

AmplifyとGitHubの接続に問題があると下記のようなエラーになったりするようです

“!!! Unable to assume specified IAM Role. Please ensure the selected IAM Role has sufficient permissions and the Trust Relationship is configured correctly.”

そのAmplifyとGitHubの接続に使用しているのが「GitHub App」という仕組みです

GitHub Appの確認方法

個人アカウントの場合

GitHub Settings → Applications → Installed GitHub Apps

Organizationの場合

Organization Settings → GitHub Apps

Organizationには下記のメニューがサイトバーにある?

  • GitHub Apps ← ここでインストール済みアプリを管理
  • OAuth Apps (OAuth Appsポリシー設定)

GitHub Apps と OAuth アプリの違い
https://docs.github.com/ja/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps

AWSのWebコンソール画面でAmplifyからGitHubへの接続について

GitHub リポジトリへの Amplify アクセスの設定
https://docs.aws.amazon.com/ja_jp/amplify/latest/userguide/setting-up-GitHub-access.html

ブラウザで現在ログインしているGitHubアカウントが自動的に使用される?

GitHub AppsとOAuth Appsの違いが分からなかった人のために、日本一わかりやすく説明してみた
https://qiita.com/dowanna6/items/cfe3fc88643d3ef95a37

一番分かりやすい OAuth の説明
https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be

WordPressサイト不正アクセスの検知

レンタルサーバー管理会社から不正アクセス検知と制限実施に関する通知

平素は当サービスをご利用いただき誠にありがとうございます。
エックスサーバーサポートでございます。

XServerアカウントID:[アカウントID]
サーバーID    :[サーバーID] ([サーバーホスト名])
ドメイン名    :[ドメイン1]
          [ドメイン2]
          [ドメイン3]
...

お問合せ番号:[お問合せ番号]


お客様のサーバーアカウントにおいて、サーバーに対する負荷が著しく高い状況を確認いたしました。
この度の負荷上昇に際してプロセスの稼働状況を確認しましたところ、以下の不正なプロセスが多数稼働しておりました。

▼稼働していた不正なプロセス
-------------------------------------------------------
Sat Sep 20 18:07:57 2025 /opt/php-7.4.33-2/bin/php /home/[サーバーID]/[ドメイン名]/public_html/l.php
-------------------------------------------------------
...



これを受け、当サポートにてセキュリティ調査を行いましたところ、
お客様がご利用のプログラムにセキュリティ上致命的なバグ(脆弱性)が存在し、
当該脆弱性を第三者に悪用されてしまった可能性が非常に高い状況でございました。

そのため、事後のご案内となり大変恐縮でございますが、緊急措置として下記制限を実施しております。

▼サポートにて実施した制限内容

・複数ドメインにおいて「WordPressセキュリティ設定」の全機能を有効化
 ▼マニュアル > WordPressセキュリティ設定
 https://www.xserver.ne.jp/manual/man_server_wpsecurity.php

 https://support.xserver.ne.jp/manual/man_server_wpsecurity.php

・複数ドメインにおいて「WAF設定」の全機能を有効化
 ▼マニュアル > WAF設定
 https://www.xserver.ne.jp/manual/man_server_waf.php

 https://support.xserver.ne.jp/manual/man_server_waf.php

・設置されていた不正プログラムファイルについて、パーミッションを「000」へ変更し、機能を無効化

[不正プログラムと思われるファイル一覧]
...

この度のような不正アクセスの被害に遭われた場合、検出された不正なファイル以外にも、
他の不正なファイルやバックドア(不正アクセスを容易とする仕組み)などが設置されている可能性が考えられます。

不正アクセスによる被害の発生を防ぐため、下記内容をご確認いただき、ご対応下さいますよう、よろしくお願いいたします。

...


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
【1】サポートにて実施したセキュリティ調査について
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

お客様のサーバーアカウントにおいて
前述の不正なファイル(ウイルス、マルウェアなど)が検出されるとともに、
日本国外からの不審なアクセスを確認いたしました。

[不審なアクセスログの一部]
------------------------------------------------------------
[ドメイン名] [IPアドレス] - - [20/Sep/2025:18:07:45 +0900] "POST /about.php?520 HTTP/1.1" 200 1750 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"
[ドメイン名] [IPアドレス] - - [20/Sep/2025:18:07:46 +0900] "POST /about.php?520 HTTP/1.1" 200 1787 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"

------------------------------------------------------------
上記ログに記載されているファイルにつきましては、
不正に設置または改ざんされている可能性が高くございます。


なお、不正アクセスの根本原因は下記パターンに大別されます。

▼不正アクセスの根本原因
------------------------------------------------------------
(1)お客様が運用中のプログラム(WordPress等)において
 [1]プログラム(WordPress等)の管理パスワードが流出し、第三者に不正ログインされた。
 [2]セキュリティ上問題のある致命的なバグ(脆弱性)が存在し、第三者に脆弱性を利用された。

 不正ログインされる、もしくは脆弱性が存在する場合、WordPressに限らず
 下記のような操作をプログラム経由で実施されてしまう可能性がございます。

 (一例)
 ・WordPressやFTP、メール等のアカウントのID、パスワードの奪取
 ・不正なアカウント、不正なファイルの設置
 ・既存ファイルの改ざん
 ・他者への攻撃の実行
 ・不正なコンテンツ、フィッシングサイトの公開・開設


(2)お客様のサーバーアカウントに関するFTP情報が流出し、
 第三者に不正にFTP接続をされた。

 →FTP操作自体によるファイル改ざんはもとより、
  任意のプログラムを設置することでどんな操作でも実行できてしまいます。
------------------------------------------------------------

お客様のサーバーアカウントにおいては不審なFTPアクセスが見られないことから、
消去法的なご案内となりますが、
プログラム(WordPress等)の管理パスワードが流出しプログラムを悪用されたか
お客様が運用中のプログラムの脆弱性を悪用されてしまった可能性が高いものと思われます。


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
【2】お客様に行っていただきたい対応内容について
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

お客様のPCや運用中のサイトのセキュリティ対策は
お客様ご自身にて管理を行っていただく責任がございます。

この度の不正アクセスについて、原因を根絶し、不正に設置されたファイルや
改ざんされたファイルをサーバーアカウント上から完全に駆逐するため、
大変お手数ですが、下記作業をなさいますようお願いいたします。

───────────────────────────────────
[1] ご利用のPCにてセキュリティチェックを行ってください。
----------------------------------------------------------------------

 お客様のご利用PC端末にてセキュリティソフトを最新版に更新していただき、
 ウイルスチェックと駆除をおこなってください。

 また、Windows UpdateやAdobe Reader、Flash Playerなどの
 ご利用PC端末にインストールされているソフトウェアにつきましても、
 最新版へ更新してください。

 ※本件はプログラムの脆弱性に起因する不正アクセスの可能性が高い状況では
  ございますが、念のため上記ご確認をお願いいたします。


───────────────────────────────────
[2] 検出されているすべてのファイルの完全削除 または、該当ドメイン名を「初期化」してください。
----------------------------------------------------------------------

 下記、ケース1、ケース2のうち【いずれか一方のみ】を実施してください。
 ※いずれの対応を行った場合でも、後続[3] ~ [5]の作業は必須となります。

 ※バックアップ機能からデータ復元をご検討のお客様へ
  ドメインの「初期化」をご対応前に、サーバーパネル「バックアップ機能」画面より
  バックアップデータが取得できることを必ずご確認いただいた上で
  作業を進めていただくようお願いいたします。
  
 ※一部のお客様環境では、ファイル数過多等による負荷が原因となり、
  自動バックアップ機能の対象から除外されております。

 ───────────────────────────────────
 ケース1■検出されているすべてのファイルの完全削除する場合 (※推奨)
 ───────────────────────────────────
  前述「サポートにて実施した制限内容」にリストアップされている
  [不正プログラムと思われるファイル一覧]に記載されている【すべてのファイル】について
  ファイルの削除を実施してください。

  ※ファイルを別の場所に移動させる、ファイル名を変更するといった対応は効果がございません。

  ※パーミッションが000となっているものを削除せず、他者が実行できる状態にすると
   不正な攻撃が再発し、サポートにおいてより強い制限を実施する可能性がございます。

  ※バックアップや作業用端末に保存されているファイルもすでに汚染されている可能性がございます。
   再発防止のため、セキュリティ上問題のない、改ざんされていないクリーンな
   データをアップロードなさいますようお願いいたいます。

 ───────────────────────────────────
 ケース2■該当ドメイン名を「初期化」する場合 ※該当ドメイン名のウェブ領域に設置されたすべてのファイルが削除されます
 ───────────────────────────────────
  「サーバーパネル」→「ドメイン設定」→該当ドメインの「初期化」と
  アクセスしていただき、「ウェブ領域・設定の初期化」をご選択のうえ、
  該当ドメイン名を初期化してください。

  ※複数のドメインが汚染されている場合は、
   不正プログラムが1つ以上設置されていたドメインについて
   ドメイン名を初期化することを推奨いたします。

  ※上記作業により、該当ドメイン名のウェブ領域に設置された
   すべてのファイルが削除されます。

   画像、プログラム、設定ファイルなどの必要なデータは
   事前にバックアップを取った上でドメイン名を初期化してください。

  ※上記作業による、データベースの初期化・削除はございません。


───────────────────────────────────
[3] FTPソフトによるデータアップロードなど、
  ホームページ再開のための作業を行ってください。
----------------------------------------------------------------------

 制限時点のデータは不正に改ざんされているデータを含む可能性や
 セキュリティ上問題がある可能性がございます。

 再発防止のため、セキュリティ上問題のない、改ざんされていないクリーンな
 データをアップロードなさいますようお願いいたいます。

 ※CGIプログラムをご利用の場合はパーミッションの変更にご注意ください。


───────────────────────────────────
[4] 該当ドメインにて設置されていたプログラムにおいて、
  脆弱性の調査を必ず行ってください。
----------------------------------------------------------------------

 不正アクセスの原因を明確にした上で、
 ホームページの運用を再開なさいますようお願いいたします。

 ※不正アクセスの原因を特定しないままホームページを再開した場合、
  再度同様の被害に遭う可能性が非常に高くなってしまいます。


 ◆「WordPress」や「Joomla!」などのCMSツールをご利用の場合、
  脆弱性が発表されていない最新バージョンを必ずご利用ください。
  旧バージョンのCMSツールには、脆弱性が報告されているケースが
  非常に多くございます。

  また、プラグインやテーマファイルにつきましても、
  最新のものを新規にインストールしていただくことを推奨いたします。

───────────────────────────────────
[5] 設置されているWordPress等の設置プログラムについて管理パスワードを変更してください。
----------------------------------------------------------------------
 
 WordPress等の設置プログラムにて、管理画面より管理パスワードの変更をお願いいたします。

 既に【パスワードが攻撃者によって特定】されており
 悪用されている可能性が非常に高い状況です。

 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 【重要】管理パスワードの変更をお願いいたします
 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 パスワード総当り(ブルートフォースアタック)による攻撃や、
 お客様が運用中のプログラムに脆弱性が存在し、
 該当脆弱性を悪用され、不正にアクセスの被害にあわれた可能性が高いものと思われます。
 
 ※これまでと同じパスワードは絶対に設定しないでください。
  また、アルファベット・数字を絡めた、第三者に推測されづらい
  パスワードをご設定ください。
 
 ※念のため、今回不正アクセスが発生しておりましたドメイン以外の
  WordPress・その他設置プログラムでも、同様に管理パスワードの変更をご検討お願いいたします。

───────────────────────────────────


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
【3】推奨される設定について
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

お客様のウェブサイトにてPHPプログラムをご利用の場合、
サーバーパネルの「php.ini設定」にて
「allow_url_fopen」および「allow_url_include」をいずれも
「無効(Off)」にすることを強くお勧めいたします。

◇マニュアル:php.ini設定
 https://www.xserver.ne.jp/manual/man_server_phpini_edit.php


※上記設定項目は「外部ファイルを読み込む/実行する」操作に対する可否設定です。

 ご利用のプログラムにより、上記それぞれの設定を「On」にする
 必要がある場合もございますが、外部からのデータ読み込み等が必要ない場合、
 これら設定は無効にしたうえでプログラムをご運用ください。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
【4】自動バックアップ機能について
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

サーバーパネル内の『自動バックアップ』機能により、
過去のデータはバックアップされており
無償でバックアップデータの取得が可能です。

バックアップデータを公開領域に設置する際には、
特に【1】でご案内した不正ファイルが含まれていないかどうか、
十分ご確認いただいた上でご利用ください。

◇自動バックアップとは?
 https://www.xserver.ne.jp/functions/service_backup.php
 https://business.xserver.ne.jp/service/detail_backup.php

◇自動バックアップからのデータ取得
 https://www.xserver.ne.jp/manual/man_server_download.php
 https://support.xserver.ne.jp/manual/man_server_download.php

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

なお、今後、同様の状況が再度確認された場合、
さらなる制限を実施する可能性がございます。

不正アクセスによる被害の発生・再発を防ぐための措置でございます。
何卒ご理解くださいますようお願いいたします。

以上、何卒よろしくお願い申し上げます。
ご不明な点などございましたら、お気軽にお問い合わせください。

◆本内容と関連するお問い合わせの際は..

どのような攻撃をされたのか?

WordPressの脆弱性を悪用して、サーバー上に不正なプログラム(Webシェル)を設置され、サーバーリソースを大量消費する攻撃を実行されています。

不正アクセスの結果発生したエラー

インシデントで発生したないようについては以下の通りです

サイトを見てみると下記のエラーが発生

  • ファイルが改ざん(ファイルの内容、ファイルの権限が原因)されているせいで発生しているエラー
  • 攻撃後のサーバー管理会社の即時対応で改ざんされたをchmod 000で無効化して読み取り不可になったためのエラー
  • エラーの対応としてはXServerの自動バックアップの取得・復元を使用
    https://www.xserver.ne.jp/manual/man_server_restore.php
  • それでも発生したエラーはプラグインをリネームして無効化して再度、名前を戻して有効化で解消

攻撃者が作成した不正ユーザー「Al1wpadmin」

知らないユーザーがあり、内容を見てみるとニックネームがなかったりサイトがhttp://wwwとかなり怪しい

「Al1wpadmin」についての記事

本インシデントでは即対応として削除しましたが、ユーザーのログを確認する必要がありますので、その後削除すればよかったかもしれません(サーバー管理画面でphpMyAdminなどでSQLで確認など、、)

-- 全ユーザーの詳細情報
SELECT 
    u.ID,
    u.user_login,
    u.user_email,
    u.user_registered,
    u.display_name,
    m.meta_value as capabilities
FROM wp_users u
LEFT JOIN wp_usermeta m ON u.ID = m.user_id 
WHERE m.meta_key = 'wp_capabilities';
-- 削除されたユーザーの投稿が残っているか確認
SELECT * FROM wp_posts WHERE post_author = '削除されたユーザーID';

-- usermeta テーブルに残骸があるか確認
SELECT * FROM wp_usermeta WHERE user_id NOT IN (SELECT ID FROM wp_users);

「XServerの自動バックアップ」は「MySQLバックアップ取得・復元」

今回の対応として「XServerの自動バックアップ」は「MySQLバックアップ取得・復元」を実施

サーバーの復元だけでなく、DBの復元もすべきか?

「XServerの自動バックアップ」は「MySQLバックアップ取得・復元」と別になります。

WordPressのユーザー情報はデータベースに保存されるためサーバーのバックアップ復元のみだと、不正ユーザーはそのまま残ってしまいます

復元元の日付より新しい投稿データなどはなくなってしまいます

カスタマーに初期化の必要性を確認

14日前の不正アクセス前の状態のバックアップから復元しましたが、他に対応すべきことはあるのでしょうか、、カスタマーに確認する予定です

今回の不正アクセスに関し、以下の対応を行いました。
サーバー領域(Web領域・Web用設定ファイル)を14日前の自動バックアップから復元
データベースも同様に14日前のバックアップから復元
不正ユーザー削除、プラグイン権限エラー対応済み
この状態でサイトは正常に動作しております。
そこで確認させていただきたいのですが、
バックアップ復元を行っていても、やはり「ドメイン初期化」を実施することが必須でしょうか。
また、初期化を省略した場合に考えられるリスクがあればご教示いただけますと幸いです。

かならずドメイン初期化が必須というわけではございません。
ただし現状は不正アクセスを受ける前の時点で復元されており、
通知させていただいている通りサイトの脆弱性は変わらず存在している状態でございます。
※WordPress等CMSのログイン情報は変更いただいている前提です。
ただ状態を前に戻しただけでは再度サイトの脆弱性を利用され
不正アクセスされる恐れがあるため、CMS本体のアップデートや、
プラグインなどプログラムのアップデートは実施いただきたく存じます。

サーバー設定の見直し

メールで下記とありました

お客様のウェブサイトにてPHPプログラムをご利用の場合、
サーバーパネルの「php.ini設定」にて
「allow_url_fopen」および「allow_url_include」をいずれも
「無効(Off)」にすることを強くお勧めいたします。

◇マニュアル:php.ini設定
 https://www.xserver.ne.jp/manual/man_server_phpini_edit.php

サーバーパネルの php.ini設定 で下記推奨のようです

外部URLからファイルを読み込めなくする
allow_url_fopen = Off
外部URLからPHPファイルを実行できなくする
allow_url_include = Off

ですが、XServerでphp.iniの設定を確認してみると下記でXアクセラレータを無効化する必要があるようです

allow_url_fopen	ON ※
allow_url_include	OFF ※
※「Xアクセラレータ Ver.2」が有効な場合、(※)の設定は変更できません。

エックスサーバーのカスタマーサポートに質問してもいいかも

【ご相談】先日のインシデントを受けてのPHPの設定について

「allow_url_fopenをOff」推奨の旨をメールで連絡いただいたのですが、
「Xアクセラレータ Ver.2」と競合するようです。
どちらを優先すべきでしょうか?

よろしくお願いいたします。
> > セキュリティを最優先する場合は、「allow_url_fopenをOff」を
> > 優先していただきますようお願いいたします。

PHP マニュアル
https://www.php.net/manual/ja/filesystem.configuration.php

Xアクセラレータ Ver.2
https://www.xserver.ne.jp/manual/man_server_xaccelerator.php

最初の侵入経路は結局何だったのか?

プラグイン周りの改ざん、不正ファイルが多かったのでプラグインの可能性が高そうでした。

カスタマーに問い合わせてみても、さすがに下記でした

申し訳ございませんが、侵入経路やファイルの特定などは
当サービスにて調査やご案内がかないません。
あらかじめご了承ください。

不正アクセスの予防策に関しましては、
下記ページもご活用くださいますと幸いです。

pnpm

Performant NPMの略で主な特徴は

  • キャッシュとリンク方式で npm / yarn より高速
  • 同じ依存は 1 回だけ保存 → ディスク節約
  • 隠れた依存を許さない → バグを防ぎやすい
特徴npmyarnpnpm
速度標準的npm より速い最速クラス
ディスク使用多い(各プロジェクトごとに保存)npm と同等少ない(依存は一度だけ保存しリンク)
依存解決の厳密さ緩い(偶然動くケースあり)少し改善厳密(隠れた依存を許さない)
モノレポ対応限定的Workspaces ありWorkspaces が強力でシンプル
セキュリティ基本機能のみ同等ライフサイクルスクリプト無効化や minimumReleaseAge で強化
普及度最も広い2番手急速に拡大中

2025 9月「npm サプライチェーン攻撃」

感染したパッケージがインストールされると、スクリプトが自動的に実行され、開発環境内に保存されている認証情報がスキャンされます。

そこからその開発者が持つあらゆる権限(リポジトリ、クラウド、公開パッケージ等)へ横展開されます。

pnpm で対策

pnpmのオプション minimumReleaseAge

公開から指定時間(分)が経過したパッケージのみをインストール可能。

Lifecycle Scriptsの無効化

Lifecycle Scripts(ライフサイクルスクリプト)」とは、npm / pnpm などのパッケージマネージャが「依存関係をインストールする過程」で自動的に呼び出すスクリプトです

pnpm 10.0.0 から、ライフサイクルスクリプトは、デフォルトでは実行されない

悪意あるスクリプトの実行も一定数は防ぐことが可能です

pnpm導入

npm でインストール

npm -v
10.9.0

npm install -g pnpm

added 1 package in 9s

pnpm -v
10.17.1

npmから移行

rm -rf node_modules

package.jsonを編集

{
  "scripts": {
    "preinstall": "npx only-allow pnpm",

上記設定でpnpm以外でのインストールを試みた場合エラーで停止します

  npm install  # ← エラーで停止
  yarn install # ← エラーで停止

pnpm install

pnpmでインストールすると再度package.jsonをよみとります

そしてパッケージを~/.pnpm-storeというグローバルストレージにダウンロードします。

node_modulesフォルダが再作成されますが、npmとは違ってシンボリックリンクを使った構造になります。

また、package-lock.jsonの代わりにpnpm-lock.yamlが生成されます。

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

状況はお名前ドットコムで取得したドメインをさくらインターネットで使用

さくらインターネットのコントロールパネルで、お名前ドットコムで取得したドメインを追加

さくらのレンタルサーバ ホーム ドメイン/SSL > メール > Webサイト/データ > ドメイン/SSL ネームサーバー設定 独自ドメイン申し込み

「他社で取得したドメインを移管せずに使う」

他社で取得したドメインを移管せずに使う キャンセル ✕ 他社で取得した独自ドメインの追加 さくらインターネット以外で取得された独自ドメインを追加します。 ドメインを追加する事により「www.」が付与されたサブドメインが同時に使用可能となります。 (例) rindev.siteを追加する事で http(s)://www.rindev.site/ での運用が可能 rindev.site サブドメインを指定する 追加

無料SSL(Let’s Encrypt)を設定したい
https://help.sakura.ad.jp/rs/2153

JPRS SSL証明書 vs Let’s Encrypt 比較表

比較項目JPRS SSL証明書Let’s Encrypt
料金年間990円〜2,200円完全無料
発行スピード最短5分数分〜数十分
有効期間1年間3ヶ月
更新作業手動または自動完全自動
設定の簡単さ申請手続きが必要ワンクリック設定
暗号化強度2048bit RSA2048bit RSA
信頼性日本の公的機関発行世界標準
メンテナンス性年1回の更新作業自動更新で放置可能

ネームサーバーとは

簡単に言うと: 「このドメインのDNS設定はどこで管理するか」を決める設定

なぜ確認が必要?

現在どちらで管理されているかによって、設定を変更する場所が変わるからです。

  • さくらのコントロールパネルで設定
  • お名前ドットコムの管理画面で設定

nslookupコマンドでお名前ドットコムかさくらインターネットでDNS設定をしているかが分かります

  • さくらインターネットの場合:ns1.dns.ne.jp、ns2.dns.ne.jp
  • お名前ドットコムの場合:01.dnsv.jp、02.dnsv.jp

お名前ドットコムでDNS設定している場合

2.ネームサーバーの選択 お名前.com その他のサービス お名前.comのネームサーバーを使う ご自身でDNSレコード設定を行う場合、転送Plus、VPS(KVM)をご利用する場合はこちらをご選択ください。 ネームサーバー1 01.dnsv.jp ネームサーバー2 02.dnsv.jp ネームサーバー3 03.dnsv.jp ネームサーバー4 04.dnsv.jp

さくらインターネットでIPアドレスを確認

さくらのレンタルサーバ ホーム ドメイン/SSL > メール > Webサイト/データ > サーバーステータス > セキュリティ > サーバー情報 > スクリプト設定 > ビジネスソリューション > メニュー一覧 サーバー情報 各種コマンドのパス SSH公開鍵 Perlモジュール情報 Python情報 移行ツール 推奨設定 プラン変更

お名前ドットコムでDNSレコードのAレコードの追加

VALUEにIPアドレスを入力して追加

入力 インポート テンプレート A/AAAA/CNAME/MX/NS/TXT/SRV/DS/CAAレコード ホスト名 TYPE TTL VALUE 優先 状態 追加 .rindev.site A 3600 . . . 有効 追加

サブドメインの場合はホスト名にサブドメイン名を入れます

入力 インポート テンプレート A/AAAA/CNAME/MX/NS/TXT/SRV/DS/CAAレコード ホスト名 TYPE TTL VALUE 優先 状態 追加 blog .rindev.site A 3600 . . . 有効 追加

SSL申し込みもDNS設定完了の反映が終わらないと下記の通りできません

rindev.site キャンセル ✕ SSLサーバー証明書のお申し込み(有料サービス) ! ご注意 • 本申し込みではファイル認証システムが適用されます。入金確認後「認証ファイル」が自動的に設置されます。 • 申請対象のFQDN(コモンネーム)は、このサーバーを参照する必要があります。 • www.example.com(例)のFQDN(コモンネーム)で申請を行った場合、example.comのURLも閲覧できる必要があります。 • 詳しい設定方法についてはサポートサイトをご確認ください。 ドメイン(www.rindev.site)の名前解決ができないためお申し込みいただけません。 ドメインを追加すると、追加された設定が反映する(利用可能になる)までに数時間~48時間程度必要です。 サーバー証明書のお申し込み

www付きサブドメインもAレコードに追加しないといけないようです

[無料SSL]設定時に表示されるエラーの対処方法を知りたい
https://faq.sakura.ad.jp/s/article/000001109?

事前準備

  • WordPressファイル一式
  • データベース
  • テーマファイル(Git管理)

DNS・サーバー設定

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

お名前ドットコムでAレコード(ドメイン名をIPアドレスに変換するDNSレコードです)を追加します

→新しく移動先のサブドメインのIPアドレスとサブドメインが紐づく

  1. お名前ドットコムのコントロールパネルにログイン
    • DNS関連機能設定を選択
  2. DNSレコード設定画面へ
    • 該当ドメインを選択
    • 「DNSレコード設定を利用する」を選択
  3. Aレコードの追加
    • レコードタイプ:A
    • ホスト名:サブドメイン名(例:blog、shop等)
    • VALUE:さくらサーバーのIPアドレス
    • TTL:3600(1時間)推奨

Aレコードとは

ドメイン名をIPアドレスに変換するDNSレコードです

example.com → 192.168.1.100

※IPアドレスはさくらレンタルサーバーのコントロールパネルで確認します

サーバー情報 → IPアドレス確認
または契約完了メールに記載されているIPアドレス

さくらのレンタルサーバでサブドメインを設定する方法!無料の独自SSLからWordPressのインストールまで解説
https://tomato-code.com/3655


独自ドメインのサブドメインを設定する(他社のネームサーバー利用)

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

さくらサーバーには「このサブドメインでアクセスされたら、このフォルダを表示する」という設定を追加する

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

WordPress移行作業

ファイル移行

  • WordPressファイル一式を新しいサブドメインディレクトリに配置
  • wp-config.phpの設定確認・調整

データベース移行

  • 新データベースにSQLファイルをインポート
  • Search Replace DB等でURL置換(旧URL→新URL)
  • wp_optionsテーブルの「siteurl」「home」を新URLに変更

リダイレクト設定

  • メインサイトの.htaccessに301リダイレクトを追加
    旧サブディレクトリ配下のすべてのページを新サブドメインに転送
RewriteRule ^subdirectory/(.*)$ https://blog.example.com/$1 [R=301,L]

動作確認・テスト

移行後の確認項目

  • 新サブドメインでのWordPress動作確認
  • 管理画面へのアクセス確認
  • テーマの表示確認
  • プラグインの動作確認
  • 内部リンクの動作確認
  • フォームやお問い合わせ機能の動作確認

SEO対策

検索エンジン対応

  • Google Search ConsoleでのURL変更通知
  • サイトマップの更新・再送信
  • 内部リンクの修正(絶対URLで記述されている場合)

スケジュール目安

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

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

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 はそのままターミナルに出力される。
でもWeb経由のPHPの場合は

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

ウェブサーバーで実行されるPHPファイルの場合

例1 echo

<!DOCTYPE html>
<html>
<body>
    <p>HTMLで出力1</p>
    
    <?php
    echo "<p>PHPで出力1</p>";
    echo "<p>PHPで出力2</p>";
    ?>
    
    <p">HTMLで出力2</p>
    
    <?php
    echo "<p>PHPで出力3</p>";
    ?>
</body>
</html>

HTMLで出力1

PHPで出力1

PHPで出力2

HTMLで出力2

PHPで出力3

例1 Warning

エラーについても標準出力で同様に上から順になります。

<?php
echo "1. 最初の出力";

// 存在しない変数でWarning発生
echo $undefined_variable;

echo "2. 次の出力";

// 存在しないファイルでWarning発生  
include 'nonexistent.php';

echo "3. 最後の出力";
?>

1. 最初の出力
Warning: Undefined variable $undefined_variable in /var/www/html/error-test-stdout.php on line 5
2. 次の出力
Warning: include(nonexistent.php): Failed to open stream: No such file or directory in /var/www/html/error-test-stdout.php on line 10

Warning: include(): Failed opening 'nonexistent.php' for inclusion (include_path='.:/usr/local/lib/php') in /var/www/html/error-test-stdout.php on line 10
3. 最後の出力
出力種別どこに出る?すぐ表示?備考
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()関数で設定したものがHTTPレスポンスヘッダーになります。

<?php
// ヘッダー設定
header('Content-Type: text/html; charset=UTF-8');
header('Location: /thanks.php');  // リダイレクト

// ボディ(標準出力)
echo "完了しました";
?>

header()は出力より前に書く必要があります。

<?php
// ❌ エラー
echo "Hello";
header('Location: /page.php');  // エラー!

// ✅ 正しい
header('Location: /page.php');
echo "Hello";
?>

↓ 解決策

<?php
ob_start();  // バッファリング開始
echo "Hello";  // まだ実際には出力されない
header('Location: /page.php');  // OK
ob_end_flush();
?>

出力のタイミング制御(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(フーイズ)は、インターネットのドメイン登録情報を照会できる仕組みです。

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

    📅 125 日前でした。

    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テストツール
    • フォーム分析ツール

    これらのタグは、トリガーと組み合わせることで、様々なイベントやユーザーアクションを追跡することができます。