blog

漢のParameterHolder(配列内のnullをどう扱うか)

2010-07-20 05:38:43 PHP Framework Comments: 0
私はSymfony 2が好きとは言えないが、Fabienの向かっている方向性が好きだ。

技術者社長としての精力的な活動。日本人ばりにワーカホリックかつ日本人よりレベルが高いと思う、
密結合による不便さの解消とDIの啓蒙(DI「コンテナ」にまで頭がまわってるかやや心配だが)、
他ライブラリの積極的な取り込み(Zend Frameworkに似通ってきたのは良い帰結だと見なす)、
法人をあげた開発体制づくり(進化を追求するあまりサポートが疎かなのは目をつぶる)、
エンタープライズフレームワークを求める姿勢(ディレクトリが深すぎるのはカスタマイズで何とかする)、
等々、欠点や偏りはあるが己の信念に向かって突き進む様は、ただただ感嘆するばかりだ。

PHPカンファレンス2009で「互換性は無いからな!」と言われたにもかかわらず、
結局カスタマイズバリバリのsymfony 1.4を愛用している身としては、
「あ、DebugPDOが無い」「あ、sfFinderが無い」など、細かいところでsymfony資産が使えない時につらくなる。
いっそのことSymfony 2を使ってsymfony 1.4を作れないものかとすら思う。

PEARのポータビリティとZend FrameworkやSymfony 2の現状ニーズ解決能力に、
Railsの「設定より規約」の思想と、(相反するが)カスタマイズ性の高さが加われば、最強じゃないかなーと思う。
パフォーマンスの最大化とデプロイ機能の強化も「最強」の要件だと思ってるけど、それは最悪自分でやる。
まあともかく将来的にはそうふう時代になって、PHP資産を扱いやすくなるであろう。わくわく。

閑話休題。いまの現実的にはsymfony 1の一部を他フレームワークに持ち込んで使うのは困難であるから、
「持ち込みやすい資産」に慣れておく/作っておくか、資産を持ち込まない方法を考えるしかない。

sfParameterHolderという、やや裏方の軽微なクラスがある。ここにもノウハウが凝縮されている。
このファイル自体に依存性は無いので、使いたいならこのファイルだけを設置すればいい。
もちろん、MITライセンスである事を明記した上で。

・・・という訳にもなかなかいかないから、いちいち手書きで実装する事が多い。
配列操作クラスを実装するには配列の直接の操作方法を理解しなければいけない。

以下にその方法を記す。
すべてPHPマニュアルに書いてあるノウハウではあるが、PHPをきちんと触った人にしかわかりにくい内容だと思う。
// 初期化(clear)
$parameters = array();

// 取得(get)
$value = $parameters[$name];

// nullを存在するとみなした上での存在確認(has)
array_key_exists($name, $parameters);

// nullやを存在しないとみなした上での存在確認
isset($parameters[$name]);

// falseやnullや""や0を存在しないとみなした上での存在確認
!empty($parameters[$name]);

// 既定値を伴う取得
// $value = (hasの内容 ? getの内容 : 既定値);
$value = array_key_exists($name, $parameters) ? $parameters[$name] : '既定値';

// 既定値の代入のみ
if (!array_key_exists($name, $parameters)) { $parameters[$name] = '既定値'; }

// 値のリスト取得
array_keys($parameters);

// 削除(remove)
unset($parameters[$name]);

// 代入(set)
$parameters[$name] = $value;

// 配列の上書き(add) 値がnullでもkeyがあれば上書きされる
array_merge($parameters, $add_parameters);

// 上書きはしないが配列に要素を追加する 値がnullでも上書きしない
$parameters += $add_parameters;

// serialize
serialize($parameters);

// unserialize
$parameters = unserialize($serialized);
PHPの構文に足りていないのは、hasとgetの部分であることがよくわかる。

「既定値を伴うget」は、addで代用できることもある。(sfActionのメンバ変数みたいなのに適用するのは微妙)
あるいはperlみたいに ||= が使えれば良かった。
// perl
$array{$name} ||= '既定値';
まあそれでも、既定値の問題は構文糖の問題に過ぎない。

しかし、hasの問題は言語仕様の根幹に関わる問題だと思う。
「何をもってして値がない状態とみなすか」という解釈が異なっており、3通りの答えが存在するからだ。

「PHPが未定義エラーを出さないこと」が定義だとすればarray_key_existsで良い。
「データベースからレコードを取ってきて、無い物は配列の既定値を適用する」なんて場合は、
issetやemptyを型などに応じて使い分けなければならない。つまり、統一的な解はない。

そういった使い分けの利便性まで考えると、あながち三項演算子作戦もデメリットばかりではないのかも知れない。

「3通りの答え」に対応した、リッチな配列操作クラスを作るのも一つの方法論だろう。
そこにはさまざまな配列機能を拡張するという余地も残されている。

しかし、PHPには配列操作クラスは無く、ただひたすら配列関数が連なっているという文化的背景を考慮すると、
あえてクラス化に頼らずに配列のままで答えを求めるのも、あるいは「漢のParameterHolder」と言えるかも知れない。
includeやクラス操作に関するオーバーヘッドを気にしなくて良くなるのも大きな利点だろう。

PHPでmysqlを適切に扱う方法

2010-07-18 01:24:36 PHP データベース Comments: 0
以前のエントリでSET NAMES問題に対して「MYSQLをやめる」ことを結論として書いた。
しかし、識者の人たちが「SET NAMESは禁止」とか「PDOは禁止」のような表現をしている事が、
この問題の理解をややこしくしているのではないかと思い直し、より実践的なまとめを書く事にした。

この記事を書き終える寸前にいいサイトを見つけてしまった。
http://d.hatena.ne.jp/jrofbyr/20081228/p1
ここの内容がほぼ結論じゃないかと思う。

というわけで、シチュエーション別にベストと思われる解決策を考えたつもりだが、以下は蛇足。
「PDOは禁止」と言われて困っている人がPDOとmysqliを比較するためのネタとして使ってくれれば大変光栄です。

データベースエンジン選定前

mysqlを使うかどうかも確定していない時は、mysqlを導入すべきかを検討しなおすと良い。
WebサーバとDBサーバを分けたい、堅牢性を求めたい時は、postgresqlが良い選択肢になる。
レプリケーションをしたい時は、Tokyo|Kyoto Tyrantが選択肢に入るかも知れない。

単純な速度比較でもpostgresqlは劣っていないし、手動vacuumも過去のものであるから、
選択可能な状況なら、迷わずpostgresqlを選択したほうが良い。

データベースエンジン選定後導入前

mysqlを使う事に決定してしまった。
phpもmysqlも「最新の安定版」を入れる事に決まったが具体的なバージョンには言及されていない。
あなたはバージョン決定に意見する事が出来るか、具体的なインストール作業を任されている。

この時あなたがするべきことは、php 5.2の出来る限り最新版を入れることだ。5.3が許されるなら尚良い。
そしてmysqlは5.1系(現在の最新安定版)を選ぶべきだ。
そして、php.iniとmy.cnfを編集し、SET NAMESを使わなくても文字化けしないような状態に設定すれば良い。

データベースエンジン導入後

phpもmysqlもバージョンが既に決まっており、インストールが済んでいる。
たとえば既存サイト上に新しいアプリを追加する時がこれに当てはまる。
アップグレードや既存システムに影響しそうな設定の変更は認められない。
しかし、スクリプト自体はこれから書く。

まず、mysqld < 5.0.7の場合は、SET NAMES問題を解決する方法は今のところ無い。
SET NAMES対象の文字コード以外がSQL文字列に一切含まれないように対策を取る必要がある。
もしもmysqld < 4.1.3なら、サーバサイドプリペアードステートメントすら使えないので、状況は絶望的である。

mysqld >= 5.0.7かつphp >= 5.1なら、PDOとサーバサイドプリペアードステートメントを使う手がある。
ただしこの時にSET NAMESと非プリペアードなSQLを併用する場合は、上記と同様の対策が必要になる。
さもなくば、PDOを使ってmy.cnfを読み込ませる手があるらしいが、正しい対処法なのかはテストしていない。
$pdo = new PDO('mysql:host=' . $host . ';dbname=' . $dbname, $user, $password, array(
    PDO::MYSQL_ATTR_READ_DEFAULT_FILE => '/var/www/my.cnf',
));
※MYSQL_ATTR_READ_DEFAULT_FILEは、Windows版だとうまく指定できないらしい?
[client]
default-character-set=sjis
mysqld >= 5.0.7かつphp >= 5.2.3なら、mysql拡張でmysql_set_charsetを使える。
この場合は逆にプリペアードステートメントが使えなくなる。

mysqld >= 5.0.7かつphp >= 5.0.5なら、mysqli拡張でmysqli_set_charsetを使える。
この場合はプリペアードステートメントも使えてSET NAMES問題も起こらない。
ただし最大の弱点として、利用者が少ないため、ノウハウの共有が困難かも知れない。
たとえば、レンタルサーバとかではmysqliを導入していない所が結構多いと思う。
開発自体は継続されているので、charset指定出来ないからPDO全否定、みたいな人はベターなのかも。
http://www.php.net/manual/ja/mysqli.overview.php

PEAR::DBを使うメリットは何も無い。
エミュレートプリペアードステートメントはSET NAMES問題の特効薬にはならないからだ。
もちろん、ある種のライブラリやラッパーがこの問題を解決する機能を提供してくれるかも知れない。
そういう観点では、PEAR::MDB2も同じだ。
しかし、MDB2には、mysqliが使えて、これを使いこなすための機能も整っているという利点がある。

「PDOをやめてmysqli(とMDB2)を使いましょう」と言われた場合に抵抗するための良い方法は思いつかない。
mysqliのラッパークラスをPDOに似せてしまうというのは如何か。
少なくとも、PDOベースのライブラリを捨ててまで、マイナーなMDB2を新規に導入する意義は無いと思う。

スクリプト実装後

既存システムのセキュリティ脆弱性を解決したい、という場合。

PDOを使っているなら、PDO::ATTR_EMULATE_PREPARES を false にする。
mysql < 5.1ではサーバサイドプリペアードステートメントを使うとクエリキャッシュが効かなくなるらしいので要注意。

PDOとSET NAMESを使っているなら、my.cnfを読み込ませる方法を確認するのが最善手かも知れない。

ORMなどを使っており、SQLを直書きで実行したい時に呼び出すメソッドが統一されているのであれば、
それが直接呼び出された時だけmysql関数を使う、という実装を考えたが、けっこう不毛な気もする。
コネクションを2本はる事になるのが良いことかどうかも悩ましい。まあアイデアの一つとして。

既存システムの改修をする際に、せめて改修対象の部分だけでもまともにしておこう、と思うならば、
とりあえずプリペアードステートメントを使うように心がけておくのが一般論としては最善手だと思う。

改善を待つべきか?

PDO_MYSQLがcharsetを適切に取り扱えるようになった時点で、PDOが最善手になる。
MDB2やmysqliを選択する事のつらさがそこにはある。php >= 5.3以降はPDOが積極的に開発される。

しかし、PDO_MYSQLには改善の兆しが無い。PHP 5.3.3RC3のソースコードを見ても対応されていない。
ひょっとするとDSNでcharsetを指定させたくないポリシーでもあるのかも知れない。
「APIの仕事を増やすなんてけしからん。pgsqlみたいに SET CLIENT_ENCODING したら動くようにしろ」って思っているとか。だとしたら反論の余地は無い。個人的にもmysqlが修正されるのが最善の方法だと思う。
(まあ、本当にそう思ってるなら、emulate_prepareも0にしたまえよ、と思うけど)
「mysql >= x.xを使えばセキュリティもパフォーマンスもサロゲートペアも全部マシになりますよ」という答えのほうが必要である。mysqlの今後の開発は、あまり期待できる状況では無いのだが・・・。

というわけで一万歩譲って「mysql関数で対応してるんだからPDOも対応しろ」という主張をすると仮定しても、
年内リリースと噂されるRHEL 6には搭載が間に合わない事が予測される。
RHEL 6のbeta2に搭載されたPHPのバージョンは5.3.2(最新版)。RHEL 5が5.1.6で、RHEL 4が4.3.9である。

最悪のケースを「mysqliの無いRHEL5」と仮定すると、
打てそうな手は結局 MYSQL_ATTR_READ_DEFAULT_FILE しか無い。

まとめ

PDOで良い。
$dbh = new PDO('mysql:host=' . $host . ';dbname=' . $dbname, $user, $password,
               array(
                     PDO::MYSQL_ATTR_READ_DEFAULT_FILE => '/etc/my.cnf',
                     PDO::MYSQL_ATTR_READ_DEFAULT_GROUP => 'pdo',
                     PDO::MYSQL_ATTR_DIRECT_QUERY => true
               ));

[pdo]
default-character-set=utf8
接続時に PDO::MYSQL_ATTR_DIRECT_QUERY を true にしていれば、
わざわざ後付けで PDO::ATTR_EMULATE_PREPARES を false にする必要は無い。

2010/07/20追記

[php-5.3.3RC3/ext/pdo_mysql/mysql_driver.c]
static int pdo_mysql_set_attribute(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC)
中略
        case PDO_MYSQL_ATTR_DIRECT_QUERY:
        case PDO_ATTR_EMULATE_PREPARES:
            ((pdo_mysql_db_handle *)dbh->driver_data)->emulate_prepare = Z_BVAL_P(val);
って書いてあるけど、これ本当にPDO::MYSQL_ATTR_DIRECT_QUERYにtrueを指定して正しく動くのか?
static int pdo_mysql_get_attribute(pdo_dbh_t *dbh, long attr, zval *return_value TSRMLS_DC)
中略
        case PDO_MYSQL_ATTR_DIRECT_QUERY:
            ZVAL_LONG(return_value, H->emulate_prepare);
            break;
 
static int pdo_mysql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC) /* {{{ */
中略
        H->emulate_prepare = pdo_attr_lval(driver_options,
            PDO_MYSQL_ATTR_DIRECT_QUERY, H->emulate_prepare TSRMLS_CC);
        H->emulate_prepare = pdo_attr_lval(driver_options,
            PDO_ATTR_EMULATE_PREPARES, H->emulate_prepare TSRMLS_CC);
なんか非常に不安なので、
PDO::MYSQL_ATTR_DIRECT_QUERY をいじるのはやめて、
PDO::ATTR_EMULATE_PREPARES を false にする方法を採用しようかと思う。

実際に動作確認を綿密にしたほうがいいな・・・。

PHPで作ったカレー

2010-07-09 23:24:05 PHP Comments: 0
野暮な解説は続きで。

カレーのはじまり

Hello <?php echo $_GET['curry'] ?>カレー

カレーに似て非なるもの

今日は<?php mysql_query('select * from curry where roux = '.$_GET['roux']) ?>カレー

ルーとライスを左右に分けたカレー

<?php
$res = $db->query('select * from curry where roux = '.$_GET['roux']);
$row = $res->fetchRow();
?>
今日は<?=$row[0] ?>カレー

器とライスを分けた欧風カレー

<?php
$res = $db->query('select * from curry where roux = '.$_GET['roux']);
$row = $res->fetchRow();
$view->assign('curry', $row[0]);
?>

今日は{curry}カレー

お母さんのカレーか何か

function cooking($tokubai) {
global $kibun;
$gu[] = buy($tokubai);
$gu[] = buy($kibun);
// ...
return call_user_func('gohan_yo');
}

密封されたレトルトカレー

<?php
$stmt = $db->prepare('select * from curry where choice = ?');
$res = $db->execute($stmt, array($_GET['choice']));
$row = $res->fetchRow();
?>
今日は<?php echo htmlspecialchars($row[0], ENT_QUOTES, 'UTF-8') ?>カレー

一箱千円の著名なレトルトカレー

管理画面にログインして「テンプレート編集」からHTMLを直接カスタマイズ出来ます。
<!--START CURRY-->
<tr class="header_line01">
<td class="cell_curry">
<?php _('This is a curry.') ?>
<?php getCurry() ?>
<?php _('This curry is end.') ?>
</td>
</tr>
<!--END CURRY-->

カップラーメンカレー味

class curryRamen extends cupRamen
{
function execute() {
$this->ramen = Ramen::doSelectOne('カレー');
$this->ramen .= Gu::findByTopping('チーズ');
}
}

作り置きのカレー

/usr/bin/php curry.php > /var/www/html/curry.html

シェフのカレー

$gu[] = new Jyagaimo();
$gu[] = new Tamanegi();
$gu[] = new Ninjin();
array_slice($gu);
$gyu = new Gyu(0.7);
$buta = new Buta(0.3);
$hikiniku = array($gyu, $buta);
shuffle($hikiniku);
$gu[] = new Niku($hikiniku);
$gu[] = new Mizu();
$curry = new Curry($gu, $roux, $spice);
$sara = new Sara($curry);

echo $sara->get('curry');

札幌スープカレー

class Nabe
{
function chouri() {
$gu[] = new Jyagaimo('北海道');
$gu[] = new Tamanegi('北海道');
$gu[] = new Ninjin('北海道');
$gu[] = new Tori('北海道');
$gu[] = new Kaisen('北海道');
$gu[] = new Spice('北海道');
$gu[] = new Mizu('北海道');
return new SoupCurry($gu);
}

愛妻カレー

new OregaImaIchibanKuitaiCurry();

APCのバグ

2010-07-09 20:07:33 PHP Comments: 0
スクリプトの内容にもよるみたいだけど一応注意。
http://pecl.php.net/bugs/bug.php?id=16721

ここに報告されている環境をまとめると、こうなる。
  • OK PHP 5.2.6 with APC 3.1.2
  • OK PHP 5.2.8 + APC 3.0.19
  • △ PHP 5.2.10 + APC 3.0.19
  • NG PHP 5.2.10 + APC 3.1.2
  • NG PHP 5.2.11 + APC 3.0.19
  • NG PHP 5.2.11 + APC 3.1.3p1
  • NG PHP 5.2.12 + APC 3.0.19
  • NG PHP 5.2.12 + APC 3.1.3p1
  • NG PHP 5.3.1 + APC 3.1.3p1
  • NG PHP 5.3.2 + APC 3.1.3p1 ※
session_write_close() を使えば直るケースもあり、使っても直らないケース※もあり。
個人的にはEC Cubeくらいでしか体験していない不具合なので、あまり深刻には考えていない。
APCは「動かないときは外しちゃう」程度の軽い気持ちであてにしたほうがいいのだろうな。

PHPとSET NAMES問題のまとめ

2010-07-09 17:55:54 PHP データベース Comments: 0
大垣さんの3年も前のエントリが元になっている議論ですが・・・。
http://blog.ohgaki.net/set_namesa_mcb_asc

意外にもネットで正しい結論が書かれていない(≒誤った理解が増えている)ので、まとめてみました。
最近どっかの本の著者がPEARを勧めて、それを鵜呑みにした人がPDOをPEAR::DBに乗り換えようとしているらしい? いや、それもう推奨されてないんですけど・・・。
変なPHPerのせいでPHPが叩かれる現象は永遠に繰り返されるのだろうか。

問題の発生条件

以下のすべて を満たすこと。
  1. データベースエンジンが mysql >= 4.1.3 である。
  2. プリペアードステートメントを使っていない。
  3. フォームから入力された値を使うSQL文が存在する。
  4. スクリプト中で SET NAMES を使って文字コードを変えている。
  5. PHP >= 5.2.3 にもかかわらずスクリプト中で mysql_set_charset を使っていない。

正しい考察手順

  1. mysql をやめる。
    • 最初にこの問題が mysql の不具合であることを認識するべきである。
    • ゆえに mysql を使わないのが最も正しい解決方法となる。どうせこの問題が無くてもmysqlは正しく4バイトのUTF-8を取り扱えないのだ。SET NAMES binary も正当な解決策ではないのだ。
    • たとえば postgresql >= 7.4であれば、この問題は起こらない。
    • mysql < 4.1.3 にはプリペアードステートメントが無いのでそもそも論外。
  2. プリペアードステートメントを使う。
    • PDOを使ってないのが理由なら、PDOを使うように変更しましょう。
    • PDO::ATTR_EMULATE_PREPARES は false にする(PHP >= 5.2.1のPDO_MYSQLではデフォルトtrueらしい。ショックだ)。
    • プ リペアードステートメントをエミュレーションするPHPライブラリには注意する。特にPHP4互換を謳うモダンフレームワークには要注意。
    • likeは WHERE name LIKE '%' || :name || '%' とか書くと安全だよ!
    • 当 たり前だけど、SQL文字列に連結する変数が全くエスケープされていないなら、SET NAMES 問題以前にセキュリティに大問題があります。
  3. バリデータを真面目に書く。
    • そもそも日本語文字列の自由記入が出来る箇所は限られている。
    • 結局プリペアードステートメントをまともに使っているかどうかの問題に帰結する。
  4. SET NAMES を使わないで済むようにmysqlクライアントとサーバの設定をする。
    • スクリプトとmysqlクライアントとmysqlサーバのエンコードを統一すれば済む問題です。
    • たとえそれがShift_JISであっても、 mysql_real_escape_string()を使えば済む問題です。
    • Shift_JISの文字化けを回避するためだけに、たとえばモバイル用サイトで「PHPとDBはEUCだけど表示はSJIS」のような構築をするのは本末転倒です。過去の亡霊にとらわれすぎ。
    • でも、UnicodeやEUC-JPが選択可能なシステムでわざわざShift_JISを選んでる人のことは「ブヲ」(侮辱の侮に半角のヲ)と呼んであげま しょう。
    • UnicodeとEUC-JPの選択で後者を選ぶ人は、サロゲートペアか文字列長か「波ダッシュ全角チルダ問題」のい ずれかで被害を受けて精神的に病んでしまった方です。いたわりの心をもって接しましょう。マジレスすると、PC専用サイトでEUC-JPに統一するのは個 人的にはアリだと思う。
  5. 以上すべての対策を満たせないときに、はじめて mysql_set_charset()を考える。
    • つまり「mysqlを使わないといけなくて」「PDOやプリペアードステートメントが使えなくて」「mysqlの設定もいじれない」状況。
    • 安物レンタルサーバで無料サンプルやら前任者の糞スクリプトを動かすような鬱になる仕事がこれに該当する。
    • mysql_query("SET NAMES 'utf8'") を mysql_set_charset('utf8') に変えるだけの簡単なお仕事です。

誤った考察

  • PHPをやめればいいと考えている。
    • ジョークや煽りならともかく真に受けてる人がいそうで恐ろしい。
    • あらゆる面でPHPを上回る代替手段を追い求めるのはロマンだから否定しない。
  • 古いmysqlを使っている。
    • mysql < 5.1ではプリペアードステートメントを使うとクエリキャッシュが効かなくなるらしい。
    • で、それを理由にプリペアードステートメント自体をやめてしまうらしい。バージョンあげろよ・・・。
  • そもそもPDOを使っていない事に疑問を抱いていない。
    • mysql関数のプリペアードステートメントは貧弱です。?しか使えません。それでいいの? いいならいいけど。
    • PHP4を使っていいのは小学生までだよねー!
    • そもそもPHP < 5.2.3には対策手段が無い。RHEL5とかでPHP 5.1.6を使ってる人はその時点で解決不能なのです。そんな環境でPHPを実行している事自体を疑問視すべき。
  • PDOは mysql_set_charset() が無いからセキュリティに不備があると考えている。
    • 「SET NAMES問題の直接的な解決策は mysql_set_charset() が最適である」という主張を見て、「mysql_set_charset()を使わない環境はすべてSET NAMES問題が起こる」と短絡しちゃった人が、こういう誤った結論に陥る。
    • どうしてもいくつかプリペアードステートメントを使えないSQL文字列を実行せざるを得ないのであれば、そのSQLだけ mysql_set_charset() を使って mysql_query() すれば良いのでは?
    • そういうSQLをなくす事のほうが重要だと思いませんか?
  • PDOよりPEAR::DB や PEAR::MDB2 のほうが良いソリューションであると考えている。
    • PEAR::DBは mysql_set_charset() に対応していません。最新版 1.7.13 (stable) was released on 2007-09-21を調べました。
    • PEAR::MDB2の最新版 2.4.1 (stable) was released on 2007-05-03 は mysql_set_charset() が実装されるPHP 5.2.3 (01-Jun-2007)よりも前にリリースされました。
    • なのでそもそも問題解決手段としては全く不適切。

結論

セキュリティ対策のために真面目にやらなければならないこと。
  • 最新安定版のPHP本体とライブラリを使うこと。
  • まともなデータベースエンジンを使うこと。もしくはデータベースエンジンを使わないこと。
  • 自分でインストールしたソフトウェアを使うか、信頼出来る人に依頼すること。
  • 誤った判断や選択をしないこと。自分の誤りを訂正できるようにしておくこと。
  • ケチらんでプロに頼んだほうがトータルで考えてお得だよ!
「セキュリティ過敏症」に掛かっている人ほど、セキュリティに関するリテラシーが低いと思う。
個人情報を実際に入力するお客様が不満を言っているならともかく、技術力の低いプログラマーが「なんちゃってセキュリティ対策」をやっちゃっても、決して誰も幸せにはならないのだ。
1 2 3 4 5 次へ 175 件中 1 ~ 5 件目