アナログCPU:5108843109

ゲームと音楽とプログラミング(酒と女とロックンロールのノリで)

('ω') < イザユケエンジニャー

アップロードされた画像をDBに保存する

作る機会があったのでメモ。
フォームのタイプとか、DBではなくファイルとして保存するとか、そういうアレンジは適当に。

手順

ざっくり分けると以下のような感じ。

  • 画像のアップロードフォームを作る
  • アップロードされた画像を加工する(リサイズなど)
  • 加工した画像をbase64化する
  • base64化したデータをDBに保存する
続きを読む

Surface Pro 4 を交換に出してみた

2016年2月に購入して以来、便利に使い倒していたSurfaceさんですが

今年に入ってから、時々画面がちらつくようになりました。

軽い時は「ちょっと一瞬ノイズが走ったように見えた」という感じ、
重い時は「画面全体がものすごいブレブレで表示内容の認識が困難」という感じ。

応急処置としては、本体を冷やすと直ることが多いです。

更新プログラムで解決できない問題だそうで、2018年5月頃から無償交換が始まりました。
https://support.microsoft.com/ja-jp/help/4230448/surface-pro-4-screen-flicker
Surface Pro 4に画面ちらつきの不具合、マイクロソフトが無償交換へ | マイナビニュース

以下の条件に該当すればリフレッシュ品との交換に応じてくれます。

  • Surface Pro 4を使っていて、ちらつきが発生する
  • 最新の状態にしても解決しない
  • 購入から3年以内である → 3年経過している場合は有償でなら対応してくれるようです

交換プロセスをざっくり書くと、以下のような感じ。

  • 問い合わせて交換受付の処理をしてもらう
  • 本体をMicrosoftに送付する
  • 5~8営業日程度で交換品が返送されてくる

「交換なのでデータなどをバックアップしておく必要がある」「数日間は手元から消える」というのがめんどくさいポイントですね。
症状も軽いし別にいっか…な人は冷やしながら使うのもアリだと思います。
わたしは開発作業してると症状がめちゃくちゃ重くなり本当に困るので今回交換に出すことにしました。
(レイヤー何十枚の高解像度絵を描いてても平気なことが多いのに、Apache起動してしばらく使うだけで、表示されているものが認識できないレベルに酷くなっていた…)

交換に応じてもらい、返ってきたものもしばらく使ってみたので、詳しい手順を残しておきます。
問い合わせしたのが2018年10月29日なのでその時点の情報ということで。

準備

問い合わせ前に、以下の準備を済ませておくとスムーズです。

  • シリアルIDを控えておく
    • 設定→システム→バージョン情報→システム情報 で確認可能
    • これさえ控えておけば、問い合わせ時は本体が手元になくてもOK
  • Microsoftアカウントのメールを確認できる状態にしておく
    • 問い合わせ時の通話中、本人確認のためにメールを受信して内容を確認する必要が出てきます
  • メモが取れるようにしておく(紙でもスマホでもPCでもいいので)

問い合わせ開始

完了までは20分程度見たほうがよいです。

電話で問い合わせます。番号は「 0120-54-2244 」。
自動の音声案内に従って操作…するのですが、真面目に聞いているとたどり着けませんでした。
問い合わせ内容によって番号を入力する形式で、「1→4→1」でOKです。

「ここに空メールするとサポート情報送るよ」とか「surfaceの交換はネットでも受け付けてるよ」とか言われますが、全部無視。
釣られてちょっと見てみたところ、今回のようなイレギュラーには対応していないようです。
最後の選択肢も、「購入から1年未満なら1、それ以上なら…」という質問ですが、問答無用で1でOKでした。
そうでなければ「有料の案内」か「フォーラムへの誘導」の二択になります。

イレギュラーにも対応してほしいっていうか、リコールみたいなもんなんだから専用の窓口用意していいレベルだと思うんだけど…。

手続きの流れ

オペレーターさんにつながるまでが一苦労でだいぶキレそうになりましたが(さすがMicrosoft)、
オペレーターさんはとても親切で分かりやすい説明をしてくれました(さすがMicrosoft)。

通話がつながったら
Surface Pro 4を使っていて、画面のちらつきが発生するので交換していただきたいのですが…」
で通じます。

順番はうろ覚えですがだいたい以下のような内容。

  • 名前と電話番号を聞かれる
  • 対象SurfaceのシリアルIDを伝え、交換対象であるかを判断してもらう
  • 本人確認(Microsoftアカウント宛にメールが来るので、本文中の確認番号を伝える)
  • 交換に際してのいろいろな説明を受ける
  • 受付番号を教えてもらう
  • 本体の送付方法や送付先を教えてもらう
  • Officeの再インストール方法を教えてもらう

受ける説明の多くは、電話後にもらえるメールに記載されています。
ただ、メールは100%届く保証があるわけではないので、最低限受付番号はメモしておきましょう。

受けた説明のまとめ

  • 本体(※1)を指定住所(※2)へゆうパック着払いで送ってね
    • 品名に受付番号を記載してね
  • 到着後5営業日後くらいに交換品を発送するよ
  • 交換だからデータは消えるよ
  • Officeも再インストールが必要だよ(※3)

※1
電話で明言されなかったような気がしますが、本体のみ。
キーボードやペンなどは不要です。
(もし誤って送った場合も返してくれるかとは思います。わたしはうっかりmicroSDカードを入れっぱなしにしていましたがご丁寧な梱包で返送いただきました)
いらない段ボールや緩衝材で梱包してやります。
これも明言されなかった気がしますが、壊れ物であることを書いておいた方がいいかも。
(例えば品名に「タブレット端末」追記するとか)

※2
メモしてね!と言われますがメールに記載されています。
 -------------------------------------------------------------
 〒143-0001 東京都大田区東海1-3-6 プロロジスパーク東京大田 3F
 Quantium Solutions(株) 気付 マイクロソフト サービス センター
 (電話番号欄には問い合わせの「0120-54-2244」でOK)
 -------------------------------------------------------------

※3
これは人によるかも。
自分の場合はMicrosoftアカウントにOfficeも紐づいていたので、
マイページからインストールするだけ、と言われました。
ちなみにインストール用に紹介された短縮URLは「 aka.ms/mya 」です。

返送された交換品を受け取ったら完了

10/29に申込み、11/7頃には受け取れる状態でした。
これをセットアップし、バックアップしておいたデータを入れなおして交換完了です。
元の端末で重たかった操作もいろいろ試してみましたが、画面のちらつきは発生せず。快適になりました。

後日談

運が悪かった。

回数を指定して文字列置換

文字列を置換するのはふつうstr_replace関数を使用しますが
PHP: str_replace - Manual
これは文中の特定文字をすべて置換することしかできません。

最初の1回だけ置換したいような場合はpreg_replaceで回数指定することができます。
第1~第3引数はいつも通りに「置換前のパターン」「置換後の文字列」「置換対象の文字列」で、
第4引数に回数を入れておくだけでOK。

<?php
// 置換前の文字列
$str  = "abcabcabc";

// 1回だけ置換
$str1 = preg_replace("/a/", "A", $str, 1); // Abcabcabc

// 2回だけ置換
$str2 = preg_replace("/a/", "A", $str, 2); // AbcAbcabc

先読み後読みと併用することで、「あるパターンより前の特定文字を1回だけ置換」「○文字目より後の特定文字を1回だけ置換」なんてことも。

<?php
// 置換前の文字列
$str  = "abcabcabc";

// あるパターンより前にある特定文字を1回だけ置換
// ※この例では存在しないパターンとなるので実際は置換されない
$str3 = preg_replace("/a(?=bcd)/", "A", $str, 1) // abcabcabc

// ○文字目より後の特定文字を1回だけ置換
$str4 = preg_replace("/(?<=.{3})a/", "A", $str, 1) // abcAbcabc

(先読み後読みについてはこちら)
正規表現の先読み・後読み - アナログCPU:5108843109

さらに、preg_replaceのパターンは配列でも指定できるので、配列指定かつ回数指定する場合の挙動はどうなってるんだろう…? と思い試してみました。

<?php
$str  = "abcabcabc";
var_dump( preg_replace(array("/a/", "/b/"), array("A", "B"), $str, 2) ); // ABcABcabc

$str = "aaabbbccc";
var_dump( preg_replace(array("/a/", "/b/"), array("A", "B"), $str, 2) ); // AAaBBbccc
var_dump( preg_replace(array("/a/", "/b/"), "X", $str, 2) ); // XXaXXbccc

配列で複数パターンを指定した場合、パターンごとに回数が適用されるようですね。

蛇足な雑談

いや、今回ハマったのが、「後読みには文字数が可変になるようなパターンを指定できない」というものでして、
具体的には、HTMLエスケープされたある文字列について、特定のタグのみエスケープを解除したいという事案でした。

例えばdivタグのみエスケープ解除するとして、

&lt;div&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;/div&gt;
&lt;div class="hoge"&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;/div&gt;

<div>
&lt;p&gt;
&lt;/p&gt;
</div>
<div class="hoge">
&lt;p&gt;
&lt;/p&gt;
</div>

としたい、ということですね。

これを一発

<?php
$str = " ... "; //(省略。エスケープ後文字列)

$pattern_before = array(
    "/&lt;(?=\/?div[\s\/>&])/",
    "/(?<=(<|\/)div[^&]*)&gt;/",
);
$pattern_after = array(
    "<",
    ">",
);
$str = preg_replace($pattern_before, $pattern_after, $str);

こんな感じで置換してしまいたかったのですが、後読みの方がうまくいかず。

仕方ないので、今回調べた内容を使って以下のように実装しました。

<?php
$str = " ... "; //(省略。エスケープ後文字列)

while (true)
{
    // divタグの位置を探す、なければ終了
    // 「&lt;(<)」と「div」の間はスラッシュのみ許容、「div」と「&gt;(>)」の間は&以外を許容
    if (!preg_match($"/&lt;\/?div[\s\/>&]/", $str, $matches, PREG_OFFSET_CAPTURE))
    {
        break;
    }
    
    // divタグが存在する場合:
    // 見つかった位置以降について、最初に見つかった&lt;と&gt;を置換するパターンを生成
    $pattern_before = array(
        "/(?<=.{" . $matches[0][1] . "})&lt;/",
        "/(?<=.{" . $matches[0][1] . "})&gt;/",
    );
    $pattern_after = array(
        "<",
        ">",
    );
    
    // 置換した文字列で$strを上書き
    $str = preg_replace(array(, ), array("<", ">"), $str, 1);
}

もっと美しい実装があるかもしれません。
あまり厳密ではないですしなんかダサい気もします。
(今回実装していたのはCMSなので厳密さは一応こんなもんで良いのですが)

ファイル名を適当にナンバリングするVBS

Windowsにて、ひとつのフォルダの中にあるファイル名を「001.***」「002.***」「003.***」…という感じで自動でナンバリングしたくて9年ほど前に作ったやつ。
その後ずっと使ってて謎の愛着のあるVBSなんですが、最近ファイル管理の命名規則変えて使わなくなったのでお蔵入りする代わりに貼っておく。

準備

  • 対象フォルダに「name.ini」というテキストファイルを用意
    • 中身は「001」とだけ入力
  • VBSファイル(コードは後述)はデスクトップでもなんでも好きなところに置いておく

使い方

  • ナンバリングしたいファイルを対象フォルダに入れる
  • ナンバリングしたい順に、VBSファイルの上にドラッグ&ドロップする

VBSのコード

無駄に長い。

続きを読む

Smartyでよく使うあれこれ

メモ。

値を表示する

基本(そのまま表示)
{$hoge}

ちなみに配列の中身を表示する場合はこんな感じでドットでつないで辿ることができます。

{$hoge.fuga}
HTMLエスケープして表示する
{$hoge|escape:'html'}
改行文字をbrタグに変換して表示する
{$hoge|nl2br}
HTMLエスケープ&改行文字をbrタグに変換して表示する
{$hoge|escape:'html'|nl2br}

逆に書くと想定通りに動きません。
たぶんbrタグに変換したやつをHTMLエスケープすることになるからだと思う。

コメントアウトする

{*
ここはコメントアウトされます
HTMLでのコメントアウト <!-- --> と違ってソースにも表示されません。
*}

他のテンプレートファイルをincludeする

{include file='header.tpl'}

ヘッダ部分など、共通化しているものを呼び出すときなど。

繰り返し処理・分岐処理

foreach
{foreach from=$hoge key=k item=v}
  <!-- 配列$hogeについて繰り返し処理 -->
  <p>{$v.id} {$v.name}</p>
{/foreach}

forもあるけどsmarty内ではまず使わないので必要になったらググる

if
{if $hoge != ""}
  <!-- $hogeが空でないとき表示 -->
  {$hoge}
{/if}

条件式の書き方はたぶんPHPと同じ。

日付フォーマット

{* 現在日時 → 任意のフォーマット *}
{$smarty.now|date_format}

{* PHPで解釈可能な日付文字列 → 任意のフォーマット *}
{strtotime($datetime)|date_format:"%Y-%m-%d %H:%M:%S"}

フォーマットに使えるやつ
date_format | Smarty

フォーマット文字列の中に日本語があるとエラーになるときもある(よく調べてない)
PHPの関数も使えるので(後述)以下なら動くけど、ちょっとどうかと思う

{date("Y年m月d日 H時i分s秒", strtotime($datetime))}

PHPの関数もまあまあ使える

やりすぎると「PHPでやれ」感出てくるけど。
だいたいifの条件式に使いがちなのでサンプルコードはifとの合わせ技。

書いてたら無限に出てきそうなので、自分の中でかなり頻出なやつを。
次点でcountとかarray_columnとかも使う。

isset

PHP側で初期化しておくのがめんどくさいときに。

{if isset($hoge)} $hogeに値が渡されている {/if}
is_array

PHP側で空配列に初期化しておくのがめんどくさいときに。

{if is_array($hoge)} $hogeは配列 {/if}
in_array

わざわざPHP側で表示用の判定のためだけの値を作りたくないときに。

{if in_array($val, $array)} 配列に含まれている {/if}

あんまり関係ないメモ

プラグインを作るという手もある。
[PHP] Smartyプラグインの作り方 – 端くれプログラマの備忘録

windowsでシンボリックリンクを作る

たまに作る機会があると毎回ぐぐってるのでメモ。

<追記>
ボケてきたのか、「シンボリックリンク」という単語がなかなか出てこなくてぐぐることすらできなくて困ってたので検索用にメモする。
シンボリックリンクとはショートカットと似て非なる、windowsでは影の薄いアレ。
</追記>

ななどら外伝2人旅シリーズ #6:サムライ×ハッカー(2020)

セブンスドラゴン2020でサムライとハッカーの2人旅。


キャラメイク

  • 「グリシーナ」ハッカー / オタクスタイル♀ / CV:ゆかな
  • 「アツタ」サムライ / スチューデントスタイル♂ / CV:中村悠一

よその13班から借りました。

f:id:honey8823:20180928104906j:plain:w400

0章(Lv1~)

  • D1:Lv--で撃破(8ターン)
  • D2:Lv--で撃破(13ターン)

レベルメモするの忘れた。でもまあたどり着いた時のレベルそのままで一発クリア。

1章(Lv6~)

  • ドラゴハンマード:Lv7、TAKE2で撃破(12ターン)
  • ウォークライ:Lv13、TAKE7で撃破(23ターン)

ドラゴハンマードではLv5→Lv7、ウォークライではLv10→Lv13と上げました。
意外とキツかった。

スキル
 グリシーナ:LF2、ATK2、DEF3、リジェネ1、サバゲ1
 アツタ:LF2、袈裟斬り4、練気2、赤火2

二人旅では意外と効いてくるLIFEボーナスを押さえておきつつ。

1.5章(Lv13~)

  • ラクモ本部改修①
  • スモウドラグ:Lv13、TAKE2で撃破(18ターン)
  • グラナロドン:Lv17、TAKE3で撃破(8ターン)

スモウドラグは初見hageたものの、いけそうだったので消費アイテム揃え直して再戦で突破。
グラナロドンはLv15でたどり着きましたがかなりキツかったので少しLv上げしてスキルも増やしてからなんとか撃破。

スキルについては、
グリシーナさんは支援スキルを強化し、アツタくんは居合コースを目指しはじめました。

2章(Lv17~)

  • ジゴワット:Lv23、TAKE2で撃破(13ターン)
  • SKY:Lv23で撃破(9ターン)

ジゴワットは、TAKE1で氷が弱点なのを忘れてて長期化した結果ジリ貧でアウト。
SKYは今回もネコから狙い、ネコは5ターン。

3章(Lv23~)

  • デストロイドラグ:Lv24、TAKE1で撃破(7ターン)
  • ロア・ア・ルア:Lv28、TAKE2で撃破(18ターン)

いつも苦戦しがちのデストロイドラグはまさかの一発クリア。
ロア・ア・ルアはLv27&Lv28で到達、一度失敗したので二人ともLv28に揃えてから挑戦でクリア。
というかTAKE2はスキルと装備揃えるの忘れたまま突っ込んだのに勝ててしまった…。

4章(Lv28~)

  • ラクモ本部改修②
  • SKY:Lv34、TAKE3で撃破(18ターン)
  • トリニトロ:Lv35で撃破(16ターン)

前回のハッカー2人旅ではめちゃくちゃ苦戦した国分寺ですが、敵の弱点ははっきりしているのでサムライがいるとむしろボーナスステージ。
SKYはネコ→ダイゴ→タケハヤの順に倒しました。

5章(Lv35~)

  • スリーピーホロウ:Lv40で撃破(11ターン)

一発クリア。これもボーナスステージですね。

6章(Lv41~)

  • ザ・スカヴァー(尾):Lv44で撃破(7ターン)
  • ザ・スカヴァー(胴):Lv44で撃破(9ターン)
  • ザ・スカヴァー(頭):Lv44で撃破(22ターン)

なんなくクリア。

7章(Lv45~)

  • ラクモ本部改修③
  • ゼロ・ブルー:Lv50、TAKE9で撃破(24ターン)

久々に苦戦。
Lv49で到達したものの、勝てそうで勝てないことが続き4回チャレンジ、Lv一つ上げてから5回チャレンジで。

X章(Lv50~)

  • クリミナルドラグ:Lv56、TAKE4で撃破(--ターン)
  • ミヅチ:Lv58で撃破(25ターン)
  • すべてのドラゴンを狩りつくした!
  • ニアラ:Lv64、TAKE7で撃破(36+9ターン)

クリミナルドラグがちょっとキツかった。
ちょっとミスると唐突に全滅する。
Lv55とLv56で2回ずつ挑戦してなんとか突破。

ミヅチはノーミス。特に危ないところもなく。

ニアラは…どうだったっけ。
メモ忘れてたんですが、確かLv62でたどり着いたような。

おしまい

前回はハッカーハッカーでしたが、片方をサムライに変えるだけですごい安定感…。

FIND_IN_SETでカンマ区切りの文字列から簡単検索

以下のような testtable テーブルについて、
カンマ区切りになっている文字列 strings を検索したい、と思うケースがあります。
あくまでカンマで区切ったひとつひとつをデータとして扱い、例えば「a」ではヒットさせたくないけど「abc」でヒットさせたい、という感じですね。

id strings
1 1,2,abc,def
2 1,3,abc,ghi,
3 ,,,

検索が発生する時点で既にこの設計は破綻しているのですが、
現実問題として「じゃあテーブル分けましょう!」と簡単にいかないことも多いので

そういうときは、MySQLであれば以下の関数を用いて検索できます。

FIND_IN_SET(検索する値, 検索対象の文字列・カラム);

主な挙動はこんな感じ。

-- SELECT * FROM `testtable` 以下
WHERE FIND_IN_SET('1'  , `strings`); -- id:1, id:2 がヒット
WHERE FIND_IN_SET(1    , `strings`); -- id:1, id:2 がヒット(数字は数値でもOK!)
WHERE FIND_IN_SET(2    , `strings`); -- id:1 のみヒット
WHERE FIND_IN_SET('abc', `strings`); -- id:1, id:2 がヒット
WHERE FIND_IN_SET('a'  , `strings`); -- ヒットしない
WHERE FIND_IN_SET('def', `strings`); -- id:1のみヒット
WHERE FIND_IN_SET(''   , `strings`); -- id:2, id:3 がヒット
WHERE FIND_IN_SET(','  , `strings`); -- ヒットしない

うわー便利。

AND検索・OR検索はいつも通り。

WHERE FIND_IN_SET('abc', `strings`) AND FIND_IN_SET('def', `strings`); -- id:1 のみヒット
WHERE FIND_IN_SET('def', `strings`) OR  FIND_IN_SET('ghi', `strings`); -- id:1, id:2 がヒット

MySQL独自関数はちょっと…という場合は前後にカンマを付けてLIKE検索。
と言いつつこの書き方もMySQL独自なので、文字列連結方法は各環境に応じてもにょもにょしましょう。
アプリ側でINSERT時に前後にカンマ付けるようにしておいても良いですけどね。

-- 「CONCAT(',', `strings`, ',')」でstringsカラムの前後にカンマを付けた文字列になる
-- 「LIKE '%,abc,%'」で「,abc,」の部分一致検索
SELECT * FROM `testtable` WHERE CONCAT(',', `strings`, ',') LIKE '%,abc,%';

雑だけど挙動はだいたい同じです。

-- SELECT * FROM `testtable` 以下
WHERE CONCAT(',', `strings`, ',') LIKE '%,1,%'  ; -- id:1, id:2 がヒット
WHERE CONCAT(',', `strings`, ',') LIKE '%,2,%'  ; -- id:1 のみヒット
WHERE CONCAT(',', `strings`, ',') LIKE '%,abc,%'; -- id:1, id:2 がヒット
WHERE CONCAT(',', `strings`, ',') LIKE '%,a,%'  ; -- ヒットしない
WHERE CONCAT(',', `strings`, ',') LIKE '%,def,%'; -- id:1のみヒット
WHERE CONCAT(',', `strings`, ',') LIKE '%,,%'   ; -- id:2, id:3 がヒット
WHERE CONCAT(',', `strings`, ',') LIKE '%,,,%'  ; -- id:3のみヒット(ここが違う)

ただしいずれにしろインデックスは効きません。効くわけない。
レコード数や実行頻度と相談して、ご利用は計画的に。
テーブルを分けることができないならフリーワード検索を応用した方がまだ良いかと思います。
(こちらはFULLTEXTインデックスを設定してればちゃんと効く)

連想配列で検索したい!!!

(2018-09-13:書いてるとおりには動いてたけど、実用を考えると破綻してたので、まるっと書き直しました)

array_columnを使えないバージョンを使わざるを得ない時用。
使えるならこちらをご参照ください。

今回は以下のような連想配列から検索することを考えます。

<?php
$data_list = array(
    1 => array('hoge' => "a"  , 'fuga' => "A"  ),
    2 => array('hoge' => "b"  , 'fuga' => "B"  ),
    3 => array('hoge' => "a"  , 'fuga' => "C"  ), // hogeが1と重複していることに注意
    4 => array('hoge' => null , 'fuga' => "D"  ),
    5 => array('hoge' => false, 'fuga' => "E"  ),
    6 => array('hoge' => "f"  , 'fuga' => null ),
    7 => array('hoge' => "g"  , 'fuga' => false),
);

検索パターンは次の3通り。

  • キーからhogeの値を求める
  • hogeからキーの値を求める
  • hogeからfugaの値を求める

これを全部まとめて関数化したい!と思うのですが、一つ問題が。

「検索結果が存在しないこと」と「検索結果がfalseであること」の戻り値を両立するの難しくない?

上記の配列で、「キーが7のfugaの値が欲しい!」となったとき、欲しい結果はもちろんfalseなんですが、
じゃあ「キーが8のfugaの値が欲しい!」と言われたら? 8は存在しないんだから、関数としてはfalseを返すべきじゃないの?

まあ、戻り値をこんな感じにすることもできますが

array(
    'not_found_flag' => 1,    // 見つからなかったよフラグ
    'result'         => null, // 検索結果
)

お手軽に検索したいから関数化するというのに、戻り値がいちいち配列なのはちょっとつらい。あとなんかかっこわるい。

連想配列の中身を検索し、キーを返す関数

ということで、そもそもひとつにまとめないパターンを考えてみました。
キーはfalseにならないのでこれなら関数としてもすっきり。

ただし、ここでは「hoge」やら「fuga」やらが存在してないケースまでは考慮してないので
必要に応じて更にバリデートを入れることになります。

あと、比較は厳密に行っています。場合によってはゆるい比較にしてもOK。

<?php
function getKey($list, $val, $key)
{
    foreach ($list as $k => $v)
    {
        if ($v[$key] === $val)
        {
            return $k;
        }
    }
    return false;
}

さっきの配列で検索してみるとこんな感じ。

<?php
var_dump( getKey($data_list, "a"  , "hoge") ); // 1 : 重複してると最初のキーを返す
var_dump( getKey($data_list, null , "hoge") ); // 4 : 値がnullの場合もOK
var_dump( getKey($data_list, false, "hoge") ); // 5 : 値がfalseの場合もOK
var_dump( getKey($data_list, "h"  , "hoge") ); // false : 存在しない
var_dump( getKey($data_list, "A"  , "fuga") ); // 1 : もちろんfugaからも検索可

あとは適宜必要に応じて戻り値がfalseかどうかをチェックすればOK。
検索結果が存在しないということはあり得ない、という場合は見なくていいし。

ということで実際に検索してみましょう。

さっきの検索したいパターンに当てはめると、

1. キーからhogeの値を求める

これはキーが分かっている状態なのでそもそもさっきの関数すら使わない。

<?php
// キー「1」の「hoge」を求める:$resは「a」
$res = $data_list[1]['hoge'];

レコードが存在しない可能性があるならこう。

<?php
if (isset($data_list[1]['hoge']))
{
    // 検索結果は $data_list[1]['hoge']
}
else 
{
    // 検索結果なし
}
2. hogeからキーの値を求める

これはつまりさっきの関数の戻り値。

<?php
// hogeが「a」のキーを求める:$resは「1」
$res = getKey($data_list, "a"  , "hoge");

レコードが存在しない可能性があるならこう。

<?php
$res = getKey($data_list, "a"  , "hoge");
if ($res !== false)
{
    // 検索結果は $res
}
else 
{
    // 検索結果なし
}
3. hogeからfugaの値を求める

1と2の合わせ技。

<?php
// hogeが「a」の「fuga」を求める:$resは「A」
$res = $data_list[getKey($data_list, "a"  , "hoge")]['fuga'];

レコードが存在しない可能性があるならこう。

<?php
$key = getKey($data_list, "a"  , "hoge");
if ($key !== false)
{
    // 検索結果は $data_list[$key]['fuga'];
}
else
{
    // 検索結果なし
}

falseのデータとかそもそもレコードが存在しないとかありえないから一発で済ませたい!!!

という場合はこちら。

キー「1」を元に、hogeの値「a」を求める

さっきと同じ。

<?php
// $resは「a」
$res = $data_list[1]['hoge'];
hogeの値「a」を元に、キーの値「1」を求める

ほい。

<?php
// $resは「1」
$res = key(array_filter(array_map(function($l){return $l['hoge'] === "a";}, $data_list)));

いきなりややこしくなりました。
ちょっと分解しましょう。

<?php
// 配列[hoge]が「a」ならtrueを返す無名関数
function($l){return $l['hoge'] === "a";}

// …をarray_mapにぶちこむ
array_map(function($l){return $l['hoge'] === "a";}, $data_list);

// その結果はtrueかfalseでできた一次元配列
//   array(1 => true, 2=> false, 3=> true)
// になるので、それをtrueでフィルタリングする
array_filter(array_map(function($l){return $l['hoge'] === "a";}, $data_list));

// trueだけになった配列
//   array(1 => true, 3=> true)
// の、現在の…っつーか何もしてないのでつまりは先頭のキーを取得
key(array_filter(array_map(function($l){return $l['hoge'] === "a";}, $data_list)));

// 完成
$res = key(array_filter(array_map(function($l){return $l['hoge'] === "a";}, $data_list)));

ちなみに存在しない値で検索しようとした場合はNULLになります。
ただ、キーがNULLになることはあり得ないので(NULLに設定しようとすると勝手に空文字になります)
NULLなら検索結果なしと判断してOK。

hogeの値「a」を元に、fugaの値「A」を求める

上記2つの組み合わせ。

<?php
// $resは「A」
$res = $data_list[key(array_filter(array_map(function($l){return $l['hoge'] === "a";}, $data_list)))]['fuga'];

これも存在しない場合はNoticeが出ます。