サーバ上のエラー発生行をブラウザからPhpStormで開く方法

こんにちは岡野です。
今回はPHPでの開発が楽になる方法を紹介します。最初の設定は面倒ですがメリットは大きいため、PhpStorm(もしくはIntelliJのPHPプラグイン)を使っている方はぜひ試してください。
   
  • 環境
    Windows
    IntelliJ IDEA 2023.2 + PHP Plugin
    Symfony(おそらく他のフレームワークでも対応可能)
    PhpStorm Protocol (最終編集2023-08-17)
  • インストール
  1. https://github.com/aik099/PhpStormProtocol のCodeボタンからzipをダウンロードする。  
  2. zip内の「PhpStorm Protocol (Win)」 を C:\Program Files\ へコピーする。  
  3. C:\Program Files\PhpStorm Protocol (Win)\run_editor.reg をダブルクリック。OK押下。  
  4. run_editor.jsを編集。上記PhpStormProtocol説明ページに載っている方法でうまくいかない場合は、editor変数を直接変更する。  
     
    editor = ‘”C:\\Users\\ユーザ名\\AppData\\Local\\Programs\\IntelliJ IDEA Ultimate\\bin\\idea64.exe”‘;
     
  5. Symfonyの設定を行う。 例は、サーバ:/opt/app/、ローカル:c:\git\app\symfony/というマッピングの場合。
(Symfony6.1以上の場合)
.env.localに以下を記述。
SYMFONY_IDE=phpstorm://open?file=%f&line=%l&/opt/app/>c:/git/app/symfony/
 
(Symfony6.0以下の場合)
framework.yaml内のframeworkにide要素を追加。
framework:
  ide: 'phpstorm://open?file=%%f&line=%%l&/opt/app/>c:/git/app/symfony/'
 
・実行
エラー発生時にファイルパスのリンクをクリックすると、該当箇所がIntelliJで開く。

Twigでも文字列連結

初めまして。石川です。
今日は、最近業務内でした新しい発見について共有させていただきます。

Twigで文字列連結するには

大したことではないのですが今までTwigでは文字列連結ができないと思って業務に取り組んでいました。
なぜなら、Twig触り始めたばかりの時に{{str1.str2}}という風に書いてエラーが出力されたからです。
その時に、きちんと調べなかったことが原因で、conntroller側で文字列連携した変数を作成してTwig側に渡すという風に切り抜けてきました。
しかし、今回きちんと調べてみると、{{str1~str2}}という風にすることで文字列連結できることを発見しました。

Twigで参照するには

ちなみに、ドットだと文字列連結ではなく参照が可能みたいです。
{% set array = {‘x’: 11, ‘y’:12} %}
{{ array.x }}
上記記載のようにすると参照できます。

Twigで変数展開するには

変数展開は{{ “aaa #{1 + 10} bbb”}}という風に、#{}で囲むことによってできます。 上記の結果は”aaa11bbb”となります。

最後に

Twig側で処理する方が、簡潔に書ける場合も多いのではないかと思うので、状況を見てTwig側に処理を書くのも良さそうです。 以上石川から共有させていただきました。

“0”はTRUEかFALSEか

 初めまして、陳です。今日は、数ヶ月前に文字列処理を行った際のミスと感想を少しご紹介したいと思います。

 文字列の値を判定する際には、有効な文字列であるかどうかや空でないかどうかを気にします。通常、渡された値がNULLか空の文字列であるかどうかをチェックします。

 そのとき、最初に思い浮かぶのはIF条件文ですよね。

 IF条件式は、プログラミングの中で最もよく使われる便利なものだと思います。条件を設定し、結果がTRUEかFALSEかによって処理を分岐させることができます。パラメータの値を基本的にチェックする際には、ほとんどの場合、欲しい値に合致しているかどうかを判断するためにIF条件式を使用します。

 FALSEかの判定に対して、最も簡単で便利な方法は、プログラミング言語におけるデータ値のTRUEかFALSEの判定を利用することです。よく知られているJavaScriptとして、FALSEと判定されるデータ値は以下の通りです:
  • JavaScript:
    – undefined
    – null
    – ブール値の false
    – 数値のゼロ: 0
    – 浮動小数点数のゼロ: 0.0
    – NaN (Not a Number)
    – 空の文字列: “”
 その時、私はそのような認識を持ち、PHPで空の文字列の判定をif(!$string)で処理していました。そして、私は間違えました。

 JavaScriptでは、文字列に対して、””(空文字列)はFALSEと判定されることを知っています。パラメータの値が空文字列でない場合、TRUEと判定されます。したがって、”a”や”0″のいずれもTRUEとなります。

 しかし、PHPの世界では少し異なります。FALSEと判定されるデータ値は以下の通りです:
  • PHP:
    – null
    – ブール値のfalse
    – 数値のゼロ: 0
    – 浮動小数点のゼロ: 0.0
    – 空の配列: []
    – 空の文字列: “”
    – 文字列”0″
 PHPでは、””(空文字列)以外にも、”0″という文字列もFALSEと判定されます。そのため、if(!$string)という条件だけでチェックすると、判定が誤ってしまうことがあります。

 文字列が空値でないのに、FALSEと判定されることに違和感を覚えたので、原因を調べてみました:

 PHPは、ウェブリクエストでの使用を前提として設計されており、頻繁に文字列の入力(URLパラメータやブラウザのフォームからのPOSTリクエストなど)を扱います。そのため、PHPは文字列を他の型に自動的にキャストします。

 これの簡単な例として、’1′ + ‘2’は3を返し、エラーや’12’などの他の解釈は行いません。同じロジックで、文字列’0’は数値0として使用することができます。

 ’0’は数値の0として使用できると言っていますが、数値の0は「空」です。PHPでは、「ゼロである」という特性を「文字列としての特性」よりも重視するため、’0’は「偽」と見なされます。

 要するに‘0’ == 0 == false もしくは (bool)’0′ === (bool)(int)’0′

(資料参考URL

 つまり、’0’という文字列は実は数値0として扱われ、そのためにFALSEと判定されるのです。
そのため、PHPで空文字列かどうかを判定したい場合には、次のような条件式を直接記述することが最善です。
  • $string === “”と空文字列を比較する
  • empty($string) というPHPの組み込み関数を使用して空値かどうかを判定する
  • strlen( $string ) === 0 と文字列の長さが0かどうかをチェックする

 以上、今回、皆さんとシェアしたい内容でした。

ファイルアップロードを伴うWebAPIの設計パターンを比べる

こんにちは、スズキです。 ファイルアップロードを伴うWebAPIを開発する機会がぼちぼちあるのですが、ファイルアップロード周りの方式をどんな設計にするかを迷いがちなので、考えを整理してみました。

方式1:multipart/form-dataを使う

ファイルアップロードの基本、multipart/form-dataをAPIリクエストに使う方式です。
  • 単純なAPIならコマンドでも比較的ポンと気軽に送信できるのが魅力
  • JSONベースのシステムから送信するときにリクエストを別途組み立てないといけないのが面倒

こういうときに採用すると良さそう

  • テキストデータが少なくファイルのやり取りがメインのAPIをつくりたいとき
  • コマンドベースで使えるようなシンプルなAPIをつくりたいとき

方式2:JSON+Base64エンコードを使う

application/jsonをAPIリクエストに使う方式です。ファイル自体のデータもBase64エンコードしてJSONデータの中に含めてしまいます。
  • JSONベースのシステムからAPIを使いやすいのが魅力
  • ファイルのエンコード・デコードの処理時間が余分にかかるのと、Base64エンコードすることでファイルサイズが増えるので通信時間が増える

こういうときに採用すると良さそう

  • ファイルサイズが小さくテキストデータのやり取りがメインのAPIをつくりたいとき
  • リクエストもレスポンスもすべてJSONでやり取りできるAPIをつくりたいとき

方式3:multipart/form-dataとJSONを使い分ける

開発するAPI群のうち、ファイルアップロード用APIだけmultipart/form-dataを使い、その他のAPIにはapplication/jsonを使う方式です。ファイルアップロード用APIでファイルを特定するためのキーを発行し、その他のAPIではそのキーを使ってファイルを指定します。
  • JSONベースのシステムから使いやすく、アップロードしたファイルを他のAPIで何度も使いまわしやすいのが魅力
  • ファイルアップロードが個別のAPIなため、APIのリクエスト回数が増える

こういうときに採用すると良さそう

  • サイズの大きいファイルと複雑なテキストデータのどちらも取り扱うAPIをつくりたいとき
  • アップロードしたファイルを何度も使いまわせるAPIをつくりたいとき

以上、どちらかというと私の備忘録な投稿でした。

文字列の途中の不要な文字列を正規表現で検知して消してみる

こんにちは。くろはです。 今回は意外と使う場面のありそう(個人的な感想)な”文字列途中の可変な不要箇所を正規表現を用いて消す”ということについて書こうかと思います。

背景


私はDBを操作する際のGUIアプリケーションとしてHeidiSQLというアプリケーションを使用しています。
HeidiSQLは単一テーブルから抽出したレコードをInsert文の形式でエクスポートできる機能があるため、
時々この機能を利用して開発環境からマスタデータをローカル環境に移してきて開発を行っています。
ただしここでそのままInsert文を実行してしまうと主キーとなっているID列が開発環境の状態のままテーブルに格納されてしまいます。
たいていは気にしなくてもよいのですがローカル環境も元々はいつぞやの時点の本番環境をコピーしたものなので開発環境のIDをそのまま使うと
自分の行っている改修とは無関係な動作を引き起こしかねない(=正しい結果が得られない)ことにつながるパターンがあったりします。
そこでid列を指定せずにレコードを挿入するとテーブル定義上は連番でIDを振ってくれるのでinsert文からID列と値の指定箇所を消してから挿入、といった作業を時々しています。
10行くらいなら「まぁ手作業でもよいか…」となりますがさすがにそれ以上は労力が見合ってくれません。 というわけで表題の正規表現で一気に指定箇所を置換してしまおうと思った次第です。

結論

このようなSQLが複数あったとしてID列とその値を1度に消します。
INSERT INTO `テスト` (`id`, `○○`, `△△`,`□□□`) VALUES (4401241, 'hoge', 'huga', 'hogehoge');
置換前と置換後の正規表現です。 置換前 (.+)id,(.+) VALUES ([0-9]*,(.+) 置換後 $1$2 VALUES ($3 置換した後のSQLは次のように変更されます。 このようなSQLが複数あったとしてID列とその値を1度に消します。
INSERT INTO `テスト` (`id`, `○○`, `△△`,`□□□`) VALUES (4401241, 'hoge', 'huga', 'hogehoge');
置換前と置換後の正規表現です。 置換前 (.+)id,(.+) VALUES ([0-9]*,(.+) 置換後 $1$2 VALUES ($3 置換した後のSQLは次のように変更されます。  
INSERT INTO `テスト` (`○○`, `△△`,`□□□`) VALUES ('hoge', 'huga', 'hogehoge');
無事ID列と値だけが消えました。  

考え方

正規表現における記号の意味等は割愛しますが置換前のパターン構造を文字で解説すると、 【文頭からidまでの任意の文字の繰り返し】 + 【`id`,】 + 【id,からVALUESまでの任意の文字の繰り返し】+【VALUES ( 【0~9の任意の数字の繰り返し+「,」】】+ 【以降文末までの任意の文字の繰り返し】 という感じです(逆に複雑か….?) 正規表現では”()”でくくると1つのグループとして評価されます。(=文字列とは判定されない) そのため「VALUES ( 」 として「 ( 」をエスケープしてあげないと正しくパターンを判定できません。 置換後の正規表現は結構シンプルで 【1つ目のグループに含まれる文字列】+【2つ目のグループに含まれる文字列】+【VALUES (】+ 【3つ目のグループに含まれる文字列】 というパターン構造になっています。 置換前のパターンの箇所で「”()”でくくるとグループとして評価されます」と書きましたが”()”にはもう一つ、後方参照という役割があります。 後方参照とはざっくりというとカッコ内のパターンに合致する文字列を記憶し、パターン内の変数の値として該当する文字列を返してくれます。 置換後の正規表現パターンの$1~$3がその変数にあたります。 これにより変更が不要な文字列をそのまま元の文字列から持ってくることができます。 青線が今回削除した部分、赤線がかっこでくくったり文字列をそのまま指定して新しく作る文字列として指定している部分です。こうやった方が分かりやすかったかも。 今回はIDの値にあたる部分がVALUES ( の後に来る文字列だったためVALUES ( をわざわざべた書きでパターンとしたけどもっと簡潔に書けたかもしれないと思えてきました。話が脱線しそうなので今回はここまでです。

終わりに

改めて見返すと正規表現のパターンの書き方って難しいけどどうパターンを組むかというのがパズルのようで楽しかったりするんですよね。ただカッコでネストしまくるとあっという間に迷宮の出来上がりなのでできるだけ簡潔に書けるように普段から意識していきたいですね。

PostmanのPre-requestって便利だなあ

こんにちわわ🐾。たにすぎです。先日APIのテストをする機会があってPostmanを利用したんですが、改めてPre-requestの機能が便利だな、と感じたので紹介します。

Postmanとは

https://www.postman.com/
Postman社が提供するAPIの開発用プラットフォームでリクエストや認証のテスト等を行えます。
Code snippetを使えばcURLやHTMLのコードにも変換してくれるので、Postmanのチームを共有していなくても他の人にリクエスト例を共有できるのでなにかと便利です。

Pre-requestって?

リクエストの前に決まった処理をしてくれる機能です。
APIを試すぞ、って時にパラメタにユニークなIDを設定しないといけなかったり、AのAPIの結果をBのAPIをリクエストに使用したい時など、手作業でパラメタ設定すると面倒な時にこのPre-requestが便利です。

ざっくり試し方

基本的なリクエストの方法は割愛します。
今回はあるAPIのリクエスト結果をPre-requestで取得して、その結果をAPIのリクエストに使います。

とりあえずENVを設定する

右上の目が付いた表みたいなアイコンを押してEnvironmentのAddをクリック
適当に変数セットの名前を入力して、Variablesに変数名を入れる

今回は環境で変わる(であろう)URLのメインの部分と1つ目のAPIの結果を入れるなんちゃらIdsを設定しています。

Pre-requestにリクエスト前の処理を書く

今回はAの結果でIDのリストのみが返ってくるのでこんな感じになりました。

const server = pm.environment.get("server"); //envからサーバー名を取得 
const xxxIdsUrl = "https://"+server+"/?limit=40";

pm.sendRequest(xxxIdsUrl, function (err, response) {
    pm.environment.set("xxxIds", response.json()); // なんちゃらIdsに結果をセット
});

envの値の取得方法が良くわからん、て場合もよく使う例が右側にSnippetsがあるので割と楽に書けました。

結果

リクエストURL横のSendを押してリクエストしてみます。

無事結果が取得できました。envのCurrent valueも更新されてます。

まとめ

使ってる方も多いかなと思いつつPostmanのPre-requestの機能を紹介しました。
Postmanで取得データのテストも書けたり、チームでコレクションを共有したりも出来るので、上手く使えば更に効率よく開発ができるんだろなと思います。
みんなで知見を共有しつつもっと活用したいですね……!

「R」を使ってみた

こんにちは、イメージマジック三浦です。最近はChatGPTで大騒ぎですが、今回は「R」を紹介します。

「R」とは

「R」は統計解析向けのプログラム言語・その開発実行環境を併せた総称です。統計学的な操作を行うためのメソッドが充実しています。特に計算式をプログラムに落とし込むような手間がなく、統計的な要件に対しては、とても使いやすいツールです。
プログラム言語と言いつつも、専門の書籍は数学関係の場所に置いてあることが多いです。

インストール

Rの公式サイトから「Download」の下にある「CRAN」をクリックすると、各国用のミラーサイトに遷移できます。遷移先のサイトから、Windows, macOS, Linux用のインストーラをダウンロードできるようになっています。 Rだけでも十分使えますが、RStudioを使うと便利です。画面表示は全部英語ですが、日本語を扱うことに問題はなく十分に使いやすいツールです。

サンプルプログラム

詳しい説明は省略しますが、参考として単回帰分析を行うコード例を出します。
data<-read.csv(
    "./_data/sample_data.csv",
    encoding = 'UTF-8',
    stringsAsFactors = F,
    header=T
)
x <- data$dataX
y <- data$dataY
result <- lm(y~x)
summary(result)
分析用のデータをきちんと準備すれば、これだけのコード量で単回帰分析を実行できます。仮に単回帰分析の導出式をプログラム言語で実装しようとすれば、サンプルコードの数倍くらい必要になるかもしれません。

計算の前後が大事

Rは、分析用データを準備すれば計算を実行して結果を返してくれますが、結果の品質はデータの準備度合いに依存します。例えば分析用データに以下のようなものが混ざっていれば、結果の精度が落ちてしまいます。
  • 全体的な傾向から逸脱しているデータがある
  • 分析に使うデータ量やパラメータが適切でない
  • データに抜けがある
このようなデータを除くために「前処理」という工程が存在し、「前処理」だけで1冊専門書が出るくらいの内容があります。 計算後にしても、予想通りの結果が得られたのか予想と異なる結果が得られたかを検証し、状況次第では分析の見直し等が必要になってきます。

まとめ

強力な機能を備えるRですが、出てきた結果を鵜呑みにせず、結果が本当に正しいかを吟味することが必要だと思います。そうできるように、これからも精進あるのみです。

ImageMagick脆弱性対応(パッチ)

こんにちは岡野です。
先日行った、画像処理ライブラリImageMagickの脆弱性対応を共有します。当社ではオリジナルプリントやMEET MY GOODSといったサービスで処理高速化のためリコンパイルしたImageMagickを使用しています。

脆弱性の概要

脆弱性(Metabase Qの報告
CVE-2022-44267:DoS攻撃
CVE-2022-44268:任意のファイル参照
ですが、不正なテキスト情報を埋め込んだPNGファイルをImageMagickが処理する際に発生する物でした。

脆弱性の対応案

・ImageMagickのバージョンアップ
ImageMagickを最新版へバージョンアップすれば解決するとのことでしたが、テストに時間が掛かるため他の方法を検討しました。

・画像データの修正
脆弱性の原因は前述の通り不正なテキスト情報ですので、ImageMagickへ画像ファイルを渡す前に不正なテキスト情報を除去する案です。PNG最適化ツールpngcrushで除去する方法をネットで見つけ試してみましたが除去処理に時間が掛かるためあきらめました(バージョンにより違うのかもしれませんが大きめの画像では10秒以上掛かることがありました)。

・ImageMagickのパッチ適用
脆弱性を修正するパッチをImageMagickのソースコードへ適用する案です。今回はこの案で対応しました。

パッチ適用の手順

今回はUbuntu用のパッチを流用しました。
https://launchpad.net/ubuntu/+source/imagemagick/8:6.9.7.4+dfsg-16ubuntu6.15
の「Available diffs」の箇所です。
但しとあるツイートに存在したPNGファイル
echo -en "P4\n1 1\nX" | pnmtopng -text <(echo 'fx:while(1,debug(hui=3)) 0') > payload.png
の脆弱性を防げなかったためパッチのif文
if ((LocaleCompare(key,"version") == 0) ||
    (LocaleCompare(key,"profile") == 0) ||
    (LocaleCompare(key,"width") == 0))
へ「fx:」を無効化する
    (LocaleNCompare(key,"fx:",3) == 0) ||
を追加しました(ImageMagickのバージョンによっては再現しなさそうでした、おそらくこのissue)。

まとめ

残念ながらImageMagickの脆弱性は今後も発見されると思いますので、バージョンアップを容易にしておく必要性を感じました。今後はバージョンアップ用の自動テストケースを準備しようと思います。

PHPのgetRealPathでファイルパスの代わりにプロジェクトルートが取れてしまった話

スズキです。 Webアプリの画面からファイル添付してサーバに送ることってありますよね。今回は、サーバ側のPHPプログラムでリクエストから添付ファイルを取り出そうとしたら、思ってたのと全然違う挙動になってしまった話です。

やろうとしたこと

やろうとしたことは超シンプルです。
  • HTTPのmultipart/form-dataで送られてきたファイルをリネームして保存する
  • ファイルが無ければエラーにする

サンプルコード(修正前)

PHP+Symfonyのコードです。例外処理などはごっそり省略。
/**
 * ファイル保存
 */
public function uploadFile(Request $request)
{
    // ①リクエストからファイルを取り出す
    $file = $request->files->get('file');
    
    // ②ファイルが一時的に保存されているパスを取得
    $tmpFilePath = $file->getRealPath();
    
    // ③ファイル存在チェック
    if (!$tmpFilePath) {
        return 'ファイルが見つかりません';
    }
    
    // ④ファイル名をリネーム
    $newFilePath = $this->makeNewPath($tmpFilePath);
    rename($tmpFilePath, $newFilePath);

    // ⑤ファイル保存
    $this->saveFile($tmpFilePath);

    return '成功しました';
}

起きたこと

②でファイルパスを取得したとき、通常なら、 // $tmpFilePath : ‘/tmp/phpucYAwE’ といった一時ファイルのパスが取得できるのですが、PHPのアップロードサイズ上限値(php.iniで設定する値)を超えたファイルを送ってみると、
// $tmpFilePath : '/opt/myWebApp/public'
といったように、なぜかWebアプリのルートディレクトリパスが取得されてしまいました。そして、③のファイル存在チェックでもエラー判定できず、そのまま④でアプリのルートディレクトリがリネームされてアプリが動かなくなる事態に…

対処

②でのファイルパス取得にて「getRealPath」を「getPathname」に変えたところ、アップロードサイズ上限を超えたファイルの場合に、期待通りに空文字が取得できるようになりました。
ファイルサイズが大きくない場合は、「getRealPath」と「getPathname」は同じパスを返してくれたので、問題なさそうです。
// $tmpFilePath : '' ←ファイルサイズ上限オーバーのときは空文字が返ってくる(期待通り)

サンプルコード(修正後)

ディレクトリパスが取得されてしまうと悲惨なので、②のパス取得部分だけでなく、安全のため③のファイル存在チェックの条件も増やしています。
/**
 * ファイル保存
 */
public function uploadFile(Request $request)
{
    // ①リクエストからファイルを取り出す
    $file = $request->files->get('file');
    
    // ②ファイルが一時的に保存されているパスを取得
    $tmpFilePath = $file->getPathname(); // getRealPath ではなく getPathname を使用
    
    // ③ファイル存在チェック
    if (!$tmpFilePath || is_dir($tmpFilePath)) { // パスがディレクトリだった場合もエラーにしておく
        return 'ファイルが見つかりません';
    }
    
    // ④ファイル名をリネーム
    $newFilePath = $this->makeNewPath($tmpFilePath);
    rename($tmpFilePath, $newFilePath);

    // ⑤ファイル保存
    $this->saveFile($tmpFilePath);

    return '成功しました';
}
 

原因推測

「getPathname」と「getRealPath」はどちらも似た説明になっていますが、「getRealPath」は絶対パスを取得するようです。 「getRealPath」が絶対パスに変換する処理の中で、空文字のパスがプロジェクトルートと解釈されたのかな…と推測しています。 リクエストに添付ファイル自体が無ければ、①の $file = $request->files->get('file'); のところでnullが取得されて特に被害は発生しないのですが、サイズ上限オーバーの場合は中途半端にファイルが存在する感じになっていそうです。 今回は事例紹介まで。

Integer型の変数がNullかどうかチェックするのはなぜ?

クロハです。 前回のPHPの初歩的な部分に躓いた話に引き続いて、たぶん今回もプログラム経験の浅い人向けの内容です。

事の発端

唐突なのですがJavaのソースの改修をしているときに次のような条件分岐に改修する必要な場面に遭遇しました。

// Hogehoge.hogeId はInteger型の変数 private boolean IsHoge(Hogehoge hogeEntity) { Integer hogeId = 0; if(hogeEntity.hogeId != null) { hogeId = hogeEntity.hogeId; } /** 以下割愛 **/ }

内容としてはhogeId に0を代入しておいて引数のhogeEntityクラスのhogeIdがNullじゃなければhogeIdに代入するというものです。他のプログラムの書き方を見ていて思いついた処理ですね。 ちなみにこの割愛している部分の処理でDBに接続してレコードを検索するのですがフレームワークの都合でhogeIdがNullだとwhere条件句からhogeIdの指定条件が消えてしまうのでhogeIdが異なっても他の条件に一致していればレコードがヒットしてしまうという事情があり上記の条件分岐と代入処理が必要でした。 この時「int」と「Integer」の違いを知らなかった私は「Integerってつまりintでしょ?nullにしようにも0しか入らなくない?」と最初は思っていました。
その後、「そもそもintにnull入れようとするとNullPointerExceptionが出るから0にすらならない..?」とか色々自分の中でも矛盾が発生したので気になって調べた、というのが今回の事の発端です。

「int」と「Integer」の違いとは

ざっくりと書くと
  • int → プリミティブ型
  • Integer → 参照型(intのラッパークラス)
ここで上記の条件の理由が分かった人はプログラミングを基礎からちゃんと理解できてる人だと思います。(というか「int null なぜ」とかググったりしないですよね..) 話を戻します。 まずラッパークラスについてですがこれも言い換えれば「基本のデータ型やオブジェクトを使いやすくするためにメソッドなどを追加したクラス」です。 あまりいい例が思いつかなかったのですが洗濯機を例にします。 洗濯の工程として 対象を投入する→洗う→干す(乾燥する)→畳む を想定すると 普通の洗濯機(基本のデータ型)では投入された対象を洗う機能しかないとなると乾燥以降の手順を自前で行う必要があります。 しかし乾燥機能付きの洗濯機(洗濯機のラッパークラスとする)であれば洗う→乾燥までの工程を洗濯機側でやってくれるので人間は対象の投入と畳む工程をやればよい、という感じですね。文明の利器バンザイ。 次にプリミティブ型と参照型についてですが、 プリミティブ型→値を持つ 参照型→メモリ上の値が格納されているアドレスを持つ という違いがあります。 こちらの記事 に良い具体例があったので引用させていただくと
int a = 1;
int b = a; // bにはaの値:1が格納される
a = 2;

Systemout.println(a); // 2 が出力される
Systemout.println(b); // 1 が出力される

int[] a = {1, 2, 3};
int[] b = a;
a[0] = 0;

Systemout.println(a[0]); // 0 が出力される
Systemout.println(b[0]); // 0 が出力される
int[] b = a の箇所ではint配列aと同じアドレスを見ているためaの値の変更が反映されていますね。 雑な結論ではありますが値が格納されているアドレスを見ているので「値の入っている場所(アドレス)が指定されていない」という状態が参照型におけるNullなのだと個人的に解釈しました。

終わりに

さて、そろそろ話を締めます。 今回学んだことを踏まえて発端となった条件分岐を考えると 引数のHogehogeエンティティのhogeIdがNullの場合は Hogehoge.hogeId に入る値のアドレスがない=アドレスが指定されていない=Null となることがあり得るということでした。 自分の備忘録的な部分がメインですが同じような疑問を持っていた方の助けになればと思います。