Overview

Packages

  • auth
  • basic
  • controller
  • db
  • form
  • html
  • paginate
  • sendmail
  • sql
  • validate

Functions

  • filter
  • form_error
  • form_get_error
  • form_has_error
  • form_set_error
  • rule
  • validate
  • Overview
  • Package
  • Function
  • Tree
   1: <?php
   2: 
   3: /*
   4:  * Copyright (c) 2014, Entap,Inc.
   5:  * All rights reserved.
   6:  * 
   7:  * Redistribution and use in source and binary forms, with or without
   8:  * modification, are permitted provided that the following conditions
   9:  * are met:
  10:  * 
  11:  * - Redistributions of source code must retain the above copyright notice,
  12:  *   this list of conditions and the following disclaimer.
  13:  * - Redistributions in binary form must reproduce the above copyright notice,
  14:  *   this list of conditions and the following disclaimer in the documentation
  15:  *   and/or other materials provided with the distribution.
  16:  * 
  17:  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  18:  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20:  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
  21:  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  22:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  23:  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  24:  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  25:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  26:  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  27:  * THE POSSIBILITY OF SUCH DAMAGE.
  28:  */
  29: //
  30: //  Photon -- a simple php library for building simple web applications
  31: //  http://entap.github.com/photon
  32: //
  33: 
  34: require_once 'photon_config.php';
  35: require_once 'config.php';
  36: 
  37: //------------------------------------------------------------------------------
  38: // 基本関数
  39: //------------------------------------------------------------------------------
  40: 
  41: /**
  42:  * 入力に対するデフォルト値の処理を行う
  43:  * 
  44:  * $valueが空文字列・NULLでない場合には$valueを返す。
  45:  * そうでない場合にはデフォルト値を返す。
  46:  * 
  47:  * 複数引数を指定した場合、空文字列・NULLではない先頭の引数の値を返す。
  48:  * 
  49:  * @param   string  $value      入力値
  50:  * @param   string  $default    デフォルト値
  51:  * @return  string  デフォルト値の処理の結果
  52:  * @package basic
  53:  */
  54: function default_value($value, $default)
  55: {
  56:     foreach (func_get_args() as $arg) {
  57:         if (!($arg === '' || $arg === NULL)) {
  58:             return $arg;
  59:         }
  60:     }
  61:     return NULL;
  62: }
  63: 
  64: /**
  65:  * 入力値の値の範囲を制限する
  66:  * 
  67:  * $valueが$minより小さい場合には$minを返す。
  68:  * $valueが$maxより大きい場合には$maxを返す。
  69:  * $valueが$min以上$max以下の範囲内の場合には$valueを返す。
  70:  * 
  71:  * @param   integer $value  入力値
  72:  * @param   integer $min    下限値
  73:  * @param   integer $max    上限値
  74:  * @return  integer 入力値の値の範囲を制限した結果
  75:  * @package basic
  76:  */
  77: function limit_range($value, $min, $max)
  78: {
  79:     if ($value < $min) {
  80:         return $min;
  81:     } elseif ($value > $max) {
  82:         return $max;
  83:     } else {
  84:         return $value;
  85:     }
  86: }
  87: 
  88: /**
  89:  * 節の処理を行う
  90:  * 
  91:  * $bodyが空文字列・NULLでない場合には、
  92:  * $prefix.$body.$suffixを連結した文字列を返す。
  93:  * そうでない場合には空文字列を返す。
  94:  * 
  95:  * @param   string  $body   入力文字列
  96:  * @param   string  $prefix 接頭辞
  97:  * @param   string  $suffix 接尾辞
  98:  * @return  string  節の処理の結果
  99:  * @package basic
 100:  */
 101: function clause($body, $prefix, $suffix)
 102: {
 103:     if ($body === '' || $body === NULL) {
 104:         return '';
 105:     } else {
 106:         return $prefix . $body . $suffix;
 107:     }
 108: }
 109: 
 110: /**
 111:  * 多次元連想配列の要素をパス形式で指定して、値を設定する
 112:  * 
 113:  * パス形式とは、要素を辿るためのキーを並べた形式である。
 114:  * 例えば、下記はいずれも$array['key1']['key2']['key3']の指定となる。
 115:  * - array('key1', 'key2', 'key3')
 116:  * - 'key1[key2][key3]'
 117:  * - 'key1.key2.key3'
 118:  * 
 119:  * @param   array   $array  対象の多次元連想配列
 120:  * @param   mixed   $name   要素を指定する文字列
 121:  * @param   mixed   $value  設定する値
 122:  * @package basic
 123:  */
 124: function array_set(&$array, $name, $value)
 125: {
 126:     if (!is_array($name)) {
 127:         $name = explode('.', str_replace(array('][', '[', ']'), '.', $name));
 128:     }
 129:     $ptr = &$array;
 130:     foreach ($name as $key) {
 131:         if ($key === '') {
 132:             continue;
 133:         }
 134:         if (!is_array($ptr)) {
 135:             $ptr = array();
 136:         }
 137:         if (!isset($ptr[$key])) {
 138:             $ptr[$key] = array();
 139:         }
 140:         $ptr = &$ptr[$key];
 141:     }
 142:     $ptr = $value;
 143: }
 144: 
 145: /**
 146:  * 多次元連想配列の要素をパス形式で指定して、値を取得する
 147:  * 
 148:  * パス形式とは、要素を辿るためのキーを並べた形式である。
 149:  * 例えば、下記はいずれも$array['key1']['key2']['key3']の指定となる。
 150:  * - array('key1', 'key2', 'key3')
 151:  * - 'key1[key2][key3]'
 152:  * - 'key1.key2.key3'
 153:  * 
 154:  * @param   array   $array  対象の多次元連想配列
 155:  * @param   mixed   $name   要素を指定する文字列
 156:  * @return  mixed   取得した値
 157:  * @package basic
 158:  */
 159: function array_get(&$array, $name)
 160: {
 161:     if (!is_array($name)) {
 162:         $name = explode('.', str_replace(array('][', '[', ']'), '.', $name));
 163:     }
 164:     $ptr = &$array;
 165:     foreach ($name as $key) {
 166:         if ($key === '') {
 167:             continue;
 168:         }
 169:         if (!is_array($ptr)) {
 170:             return NULL;
 171:         }
 172:         if (!isset($ptr[$key])) {
 173:             return NULL;
 174:         }
 175:         $ptr = &$ptr[$key];
 176:     }
 177:     return $ptr;
 178: }
 179: 
 180: /**
 181:  * 相対パスを解決する
 182:  * 
 183:  * $baseを基準パスとして、$relativeの相対パス指定を解決する。
 184:  * 相対パス指定として、親ディレクトリ..や現在ディレクトリ.が使用できる。
 185:  * 
 186:  * $relativeが/で始まる場合、絶対パスが指定されたとみなし、
 187:  * $baseは無視される。
 188:  * 
 189:  * $baseにファイル名が指定された場合、ファイルの親ディレクトリが基準パスとなる。
 190:  * 
 191:  * @param   string  $relative   相対パス
 192:  * @param   string  $base       基準パス
 193:  * @return  string  相対パスを解決した結果
 194:  * @package basic
 195:  */
 196: function relative_path($relative, $base)
 197: {
 198:     // 処理対象のパスを求める
 199:     if (substr($base, -1) != '/') {
 200:         $base = dirname($base);
 201:     }
 202:     if (substr($relative, 0, 1) == '/') {
 203:         $path = $relative;
 204:     } else {
 205:         $path = $base . '/' . $relative;
 206:     }
 207: 
 208:     // 相対パスを解決する
 209:     $dirs = explode('/', $path);
 210:     $stack = array();
 211:     $depth = 0;
 212:     foreach ($dirs as $dir) {
 213:         if ($dir === '' || $dir === '.') {
 214:             continue;
 215:         } elseif ($dir === '..') {
 216:             if (count($stack) == 0) {
 217:                 $depth++;
 218:             } else {
 219:                 array_pop($stack);
 220:             }
 221:         } else {
 222:             array_push($stack, $dir);
 223:         }
 224:     }
 225: 
 226:     // 結果のパスを生成
 227:     if (substr($base, 0, 1) == '/') {
 228:         return '/' . implode('/', $stack);
 229:     } else {
 230:         return str_repeat('../', $depth) . implode('/', $stack);
 231:     }
 232: }
 233: 
 234: /**
 235:  * URLの構成要素からURLを表す文字列を生成する
 236:  * 
 237:  * @param   array   $url_parsed URLの構成要素
 238:  * @return  string  URLを表す文字列
 239:  * @package basic
 240:  */
 241: function build_url($url_parsed)
 242: {
 243:     if (!is_array($url_parsed)) {
 244:         return $url_parsed;
 245:     }
 246: 
 247:     // スキーム
 248:     if (isset($url_parsed['scheme'])) {
 249:         if (strcasecmp($url_parsed['scheme'], 'mailto') == 0) {
 250:             $url = $url_parsed['scheme'] . ':';
 251:         } else {
 252:             $url = $url_parsed['scheme'] . '://';
 253:         }
 254:     } else {
 255:         $url = '';
 256:     }
 257: 
 258:     // ユーザ名・パスワード
 259:     if (isset($url_parsed['user'])) {
 260:         $url .= $url_parsed['user'];
 261:         if (isset($url_parsed['pass'])) {
 262:             $url .= ':' . $url_parsed['pass'];
 263:         }
 264:         $url .= '@';
 265:     }
 266: 
 267:     // ホスト名
 268:     if (isset($url_parsed['host'])) {
 269:         $url .= $url_parsed['host'];
 270:         if (isset($url_parsed['port'])) {
 271:             $url .= ':' . $url_parsed['port'];
 272:         }
 273:     }
 274: 
 275:     // パス
 276:     $url .= '/';
 277:     if (isset($url_parsed['path'])) {
 278:         $url .= ltrim($url_parsed['path'], '/');
 279:     }
 280: 
 281:     // クエリ
 282:     if (isset($url_parsed['query'])) {
 283:         $url .= '?' . $url_parsed['query'];
 284:     }
 285: 
 286:     // フラグメント
 287:     if (isset($url_parsed['fragment'])) {
 288:         $url .= '#' . $url_parsed['fragment'];
 289:     }
 290: 
 291:     return $url;
 292: }
 293: 
 294: /**
 295:  * URLのクエリ文字列を連想配列から生成する
 296:  * 
 297:  * @param   array   $data   URLのクエリを表す連想配列
 298:  * @param   string  $prefix クエリデータの接頭辞
 299:  * @return  string  URLのクエリ文字列
 300:  * @package basic
 301:  */
 302: function build_url_query($data, $prefix = NULL)
 303: {
 304:     $query = '';
 305:     foreach ($data as $key => $value) {
 306:         if ($prefix === NULL) {
 307:             $name = urlencode($key);
 308:         } else {
 309:             $name = $prefix . '[' . urlencode($key) . ']';
 310:         }
 311:         if (is_array($value)) {
 312:             $query .= build_url_query($value, $name) . '&';
 313:         } else {
 314:             $query .= $name . '=' . urlencode($value) . '&';
 315:         }
 316:     }
 317:     return substr($query, 0, -1);
 318: }
 319: 
 320: /**
 321:  * 相対URLを解決する
 322:  * 
 323:  * $baseを基準URLとして、$relativeの相対URL指定を解決する。
 324:  * $relativeにはparse_urlでパースできるURLが使用できる。
 325:  * 
 326:  * @param   string  $relative   相対URL
 327:  * @param   string  $base       基準URL
 328:  * @return  string  相対URLを解決した結果
 329:  * @package basic
 330:  */
 331: function relative_url($relative, $base)
 332: {
 333:     $r = parse_url($relative);
 334:     $b = parse_url($base);
 335:     if (isset($r['scheme'])) {
 336:         // $relativeにスキームが指定された場合
 337:         return $relative;
 338:     } elseif (isset($r['host'])) {
 339:         // $relativeにホスト名が指定された場合
 340:         $u = $r;
 341:         $u['scheme'] = $b['scheme'];
 342:     } elseif (isset($r['path'])) {
 343:         // $relativeにパスが指定された場合
 344:         $u = $b;
 345:         $u['path'] = relative_path($r['path'], $b['path']);
 346:         if (isset($r['query'])) {
 347:             $u['query'] = $r['query'];
 348:         } else {
 349:             unset($u['query']);
 350:         }
 351:         if (isset($r['fragment'])) {
 352:             $u['fragment'] = $r['fragment'];
 353:         } else {
 354:             unset($u['fragment']);
 355:         }
 356:     } elseif (isset($r['query'])) {
 357:         // $relativeにクエリが指定された場合
 358:         $u = $b;
 359:         $u['query'] = $r['query'];
 360:         if (isset($r['fragment'])) {
 361:             $u['fragment'] = $r['fragment'];
 362:         } else {
 363:             unset($u['fragment']);
 364:         }
 365:     } elseif (isset($r['fragment'])) {
 366:         // $relativeにフラグメントが指定された場合
 367:         $u = $b;
 368:         $u['fragment'] = $r['fragment'];
 369:     } else {
 370:         return $base;
 371:     }
 372:     return build_url($u);
 373: }
 374: 
 375: /**
 376:  * URLのクエリをマージする
 377:  * 
 378:  * @param   string  $url    元のURL
 379:  * @param   mixed   $query  マージするクエリの文字列、または連想配列
 380:  * @return  string  生成したURLを表す文字列
 381:  * @package basic
 382:  */
 383: function modify_url_query($url, $query)
 384: {
 385:     // URLをパース
 386:     if (!is_array($url)) {
 387:         $url = parse_url($url);
 388:     }
 389: 
 390:     // クエリをマージ
 391:     if (isset($url['query'])) {
 392:         parse_str($url['query'], $array1);
 393:     } else {
 394:         $array1 = array();
 395:     }
 396:     if (is_array($query)) {
 397:         $array2 = $query;
 398:     } else {
 399:         parse_str($query, $array2);
 400:     }
 401:     $url['query'] = build_url_query(array_merge($array1, $array2));
 402: 
 403:     // URLを構築
 404:     return build_url($url);
 405: }
 406: 
 407: /**
 408:  * 現在のリクエストURLを取得する
 409:  * 
 410:  * @return  string  現在のリクエストURLを表す文字列
 411:  * @package basic
 412:  */
 413: function get_request_url()
 414: {
 415:     // スキーム
 416:     if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') {
 417:         $url = 'https://';
 418:         $port = 443;
 419:     } else {
 420:         $url = 'http://';
 421:         $port = 80;
 422:     }
 423: 
 424:     // ホスト名
 425:     if (isset($_SERVER['HTTP_HOST'])) {
 426:         $url .= $_SERVER['HTTP_HOST'];
 427:     } elseif (isset($_SERVER['SERVER_NAME'])) {
 428:         $url .= $_SERVER['SERVER_NAME'];
 429:     } elseif (isset($_SERVER['SERVER_ADDR'])) {
 430:         $url .= $_SERVER['SERVER_ADDR'];
 431:     }
 432: 
 433:     // ポート番号
 434:     if (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] != $port) {
 435:         $url .= ':' . $_SERVER['SERVER_PORT'];
 436:     }
 437: 
 438:     // パス
 439:     $url .= '/';
 440:     if (isset($_SERVER['REQUEST_URI'])) {
 441:         $url .= ltrim(strtok($_SERVER['REQUEST_URI'], '?'), '/');
 442:     } elseif (isset($_SERVER['PHP_SELF'])) {
 443:         $url .= ltrim(htmlspecialchars($_SERVER['PHP_SELF']), '/');
 444:     }
 445: 
 446:     // クエリ
 447:     if (isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING'] !== '') {
 448:         $url .= '?' . $_SERVER['QUERY_STRING'];
 449:     }
 450: 
 451:     return $url;
 452: }
 453: 
 454: /**
 455:  * 現在のリクエストがPOSTかを調べる
 456:  * 
 457:  * @return  boolean 現在のリクエストがPOSTならTRUE、そうでない場合にはFALSE
 458:  * @package basic
 459:  */
 460: function is_request_post()
 461: {
 462:     return strcasecmp($_SERVER['REQUEST_METHOD'], 'post') == 0 ? TRUE : FALSE;
 463: }
 464: 
 465: /**
 466:  * 変数の真偽値を調べる
 467:  * 
 468:  * $varが真偽値のTRUE、または文字列のyes、y、true、tである場合にはTRUE、
 469:  * そうでない場合にはFALSEを返す。
 470:  * 
 471:  * @param   mixed   $var    対象の変数
 472:  * @return  boolean $varの真偽値
 473:  * @package basic
 474:  */
 475: function is_true($var)
 476: {
 477:     if (is_numeric($var)) {
 478:         return intval($var) ? TRUE : FALSE;
 479:     } else if (is_string($var)) {
 480:         $char = substr($var, 0, 1);
 481:         return $char == 'y' || $char == 't' || $char == 'on' ? TRUE : FALSE;
 482:     } else {
 483:         return $var ? TRUE : FALSE;
 484:     }
 485: }
 486: 
 487: /**
 488:  * 文字列がメールアドレスとして正しいか調べる
 489:  * 
 490:  * @param   string  $str    対象の文字列
 491:  * @return  boolean 文字列がメールアドレスとして正しい場合にはTRUE。
 492:  *                  そうでない場合にはFALSE
 493:  * @package basic
 494:  */
 495: function is_mail($str)
 496: {
 497:     return preg_match('/^[a-z0-9._+^~-]+@[a-z0-9.-]+$/i', $str) ? TRUE : FALSE;
 498: }
 499: 
 500: /**
 501:  * 文字列がIPv4アドレスとして正しいか調べる
 502:  * 
 503:  * @param   string  $str    対象の文字列
 504:  * @return  boolean 文字列がIPv4アドレスとして正しい場合にはTRUE。
 505:  *                  そうでない場合にはFALSE
 506:  * @package basic
 507:  */
 508: function is_ipv4($str)
 509: {
 510:     return long2ip(ip2long($str)) === $str ? TRUE : FALSE;
 511: }
 512: 
 513: /**
 514:  * テンプレート文字列に変数を埋め込む
 515:  * 
 516:  * テンプレート文字列には{key1.key2}の形式で変数を指定でき、
 517:  * $vars[$key1][$key]の値で置換される。
 518:  * $filterを指定した場合、$filterが変換関数として使用される。
 519:  * 
 520:  * @param   string  $template   テンプレート文字列
 521:  * @param   array   $vars       変数の連想配列
 522:  * @param   string  $filter     フィルタ関数
 523:  * @return  string  テンプレート文字列に変数を埋め込んだ結果
 524:  * @package basic
 525:  */
 526: function embed($template, $vars, $filter = NULL)
 527: {
 528:     global $__photon_vars;
 529:     global $__photon_filter;
 530:     $__photon_vars = $vars;
 531:     $__photon_filter = $filter;
 532:     return preg_replace_callback('/{([a-zA-Z0-9._]+)}/', '__embed', $template);
 533: }
 534: 
 535: /** @ignore */
 536: function __embed($matches)
 537: {
 538:     global $__photon_vars;
 539:     global $__photon_filter;
 540:     $value = array_get($__photon_vars, $matches[1]);
 541:     if ($__photon_filter === NULL) {
 542:         return $value;
 543:     } else {
 544:         return call_user_func($__photon_filter, $value);
 545:     }
 546: }
 547: 
 548: /**
 549:  * 文字列の改行コードを置換する
 550:  * 
 551:  * @param   string  $str        対象の文字列
 552:  * @param   string  $newline    改行コード
 553:  * @return  string  改行コードを置換した結果
 554:  * @package basic
 555:  */
 556: function convert_newline($str, $newline = "\n")
 557: {
 558:     $str = str_replace(array("\r\n", "\r", "\n"), "\n", $str);
 559:     if ($newline == "\n") {
 560:         return $str;
 561:     } else {
 562:         return str_replace("\n", $newline, $str);
 563:     }
 564: }
 565: 
 566: /**
 567:  * ファイルの拡張子を取得する
 568:  * 
 569:  * @param   string  $filename   ファイル名
 570:  * @return  string  ファイルの拡張子
 571:  * @package basic
 572:  */
 573: function extension($filename)
 574: {
 575:     $str = strrchr($filename, '.');
 576:     if ($str === FALSE) {
 577:         return NULL;
 578:     } else {
 579:         return substr($str, 1);
 580:     }
 581: }
 582: 
 583: /**
 584:  * アンダースコアで区切った文字列をキャメルケースの文字列に変換する
 585:  * 
 586:  * @param   string  $str        アンダースコアで区切った文字列
 587:  * @param   string  $lcfirst    先頭の文字を小文字にするか?
 588:  * @return  string  キャメルケースの文字列
 589:  * @package basic
 590:  */
 591: function camelize($str, $lcfirst = true)
 592: {
 593:     $str = str_replace(' ', '', ucwords(str_replace('_', ' ', $str)));
 594:     if ($lcfirst) {
 595:         $str = lcfirst($str);
 596:     }
 597:     return $str;
 598: }
 599: 
 600: /**
 601:  * キャメルケースの文字列をアンダースコアで区切った文字列に変換する
 602:  * 
 603:  * @param   string  $str    キャメルケースの文字列
 604:  * @return  string  アンダースコアで区切った文字列
 605:  * @package basic
 606:  */
 607: function underscore($str)
 608: {
 609:     return strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $str));
 610: }
 611: 
 612: /**
 613:  * 配列の全要素のキーに対して、ユーザ関数を適用する
 614:  * 
 615:  * @param   array       $array      対象の配列
 616:  * @param   callable    $callback   キーに対して適用するユーザ関数
 617:  * @param   mixed       $userdata   ユーザ関数の引数
 618:  * @return  array       変換結果の配列
 619:  * @package basic
 620:  */
 621: function array_change_key($array, $callback, $userdata = NULL)
 622: {
 623:     $result = array();
 624:     foreach ($array as $key => $value) {
 625:         $key = call_user_func($callback, $key, $userdata);
 626:         if (is_array($value)) {
 627:             $result[$key] = array_change_key($value, $callback, $userdata);
 628:         } else {
 629:             $result[$key] = $value;
 630:         }
 631:     }
 632:     return $result;
 633: }
 634: 
 635: /**
 636:  * 設定オプションの値を設定する
 637:  * 
 638:  * 引数を一つだけ指定した場合には、設定オプションの値を取得する
 639:  * 
 640:  * @param   string  $name   設定オプションの名前
 641:  * @param   string  $value  設定オプションの値
 642:  * @return  string  取得した設定オプションの値
 643:  * @package basic
 644:  */
 645: function config($name, $value = NULL)
 646: {
 647:     global $__photon_config;
 648:     if (func_num_args() == 1) {
 649:         if (isset($__photon_config[$name])) {
 650:             return $__photon_config[$name];
 651:         } else {
 652:             return NULL;
 653:         }
 654:     } else {
 655:         return $__photon_config[$name] = $value;
 656:     }
 657: }
 658: 
 659: //------------------------------------------------------------------------------
 660: // HTMLタグ
 661: //------------------------------------------------------------------------------
 662: 
 663: /**
 664:  * HTML開始タグを生成する
 665:  * 
 666:  * @param   string  $name       タグの名前
 667:  * @param   mixed   $attributes タグの属性の文字列、または連想配列
 668:  * @param   boolean $is_empty   空タグの場合にはTRUE、そうでない場合にはFALSE
 669:  * @return  string  生成した開始タグ
 670:  * @package html
 671:  */
 672: function tag_open($name, $attributes = NULL, $is_empty = FALSE)
 673: {
 674:     $attributes = tag_build_attributes($attributes);
 675:     $str = '<' . htmlspecialchars($name);
 676:     if ($attributes !== '') {
 677:         $str .= ' ' . $attributes;
 678:     }
 679:     if ($is_empty) {
 680:         return $str . ' />';
 681:     } else {
 682:         return $str . '>';
 683:     }
 684: }
 685: 
 686: /**
 687:  * HTML終了タグを生成する
 688:  * 
 689:  * @param   string  $name       タグの名前
 690:  * @return  string  生成した終了タグ
 691:  * @package html
 692:  */
 693: function tag_close($name)
 694: {
 695:     return '</' . htmlspecialchars($name) . '>';
 696: }
 697: 
 698: /**
 699:  * HTMLタグの属性文字列をパースする
 700:  * 
 701:  * 例えば、$strに文字列'key1="value1" key2="value2"'を指定した場合、
 702:  * array('key1'=>'value1', 'key2'=>'value2')を返す。
 703:  * 
 704:  * @param   string  $str    タグの属性の文字列
 705:  * @return  array   タグの属性の連想配列
 706:  * @package html
 707:  */
 708: function tag_parse_attributes($str)
 709: {
 710:     if (is_array($str)) {
 711:         return $str;
 712:     }
 713:     if ($str === NULL) {
 714:         return array();
 715:     }
 716:     $array = array();
 717:     $strlen = strlen($str);
 718:     $spaces = " \t\r\n";
 719:     $i = 0;
 720:     while ($i < $strlen) {
 721:         $i += strspn($str, $spaces, $i);
 722:         $n = strcspn($str, $spaces . '=', $i);
 723:         $key = substr($str, $i, $n);
 724:         $i += $n;
 725:         $i += strspn($str, $spaces, $i);
 726:         if (substr($str, $i, 1) == '=') {
 727:             $i++;
 728:             $i += strspn($str, $spaces, $i);
 729:             if (substr($str, $i, 1) == '"') {
 730:                 $i++;
 731:                 $n = strcspn($str, '"', $i);
 732:                 $value = substr($str, $i, $n);
 733:                 $i += $n + 1;
 734:             } else {
 735:                 $n = strcspn($str, $spaces, $i);
 736:                 $value = substr($str, $i, $n);
 737:                 $i += $n;
 738:             }
 739:         } else {
 740:             $value = $key;
 741:         }
 742:         $array[$key] = $value;
 743:     }
 744:     return $array;
 745: }
 746: 
 747: /**
 748:  * HTMLタグの属性文字列を生成する
 749:  * 
 750:  * 例えば、$arrayに連想配列array('key1'=>'value1', 'key2'=>'value2')を
 751:  * 指定した場合、文字列'key1="value1" key2="value2"'を返す。
 752:  * 
 753:  * @param   array   $array  タグの属性の連想配列
 754:  * @return  string  タグの属性の文字列
 755:  * @package html
 756:  */
 757: function tag_build_attributes($array)
 758: {
 759:     if (is_string($array)) {
 760:         return $array;
 761:     }
 762:     if ($array === NULL) {
 763:         return '';
 764:     }
 765:     $str = '';
 766:     foreach ($array as $key => $value) {
 767:         $str .= htmlspecialchars($key) . '=';
 768:         $str .= '"' . htmlspecialchars($value) . '" ';
 769:     }
 770:     return rtrim($str, ' ');
 771: }
 772: 
 773: /**
 774:  * &lt;input&gt;タグを生成する
 775:  * 
 776:  * @param   string  $type       type属性の値
 777:  * @param   string  $name       name属性の値
 778:  * @param   string  $value      value属性の値
 779:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
 780:  * @return  string  生成したHTMLタグ
 781:  * @package html
 782:  */
 783: function tag_input($type, $name, $value, $attributes = NULL)
 784: {
 785:     $attributes = tag_parse_attributes($attributes);
 786:     if (!isset($attributes['type'])) {
 787:         $attributes['type'] = $type;
 788:     }
 789:     if (!isset($attributes['name'])) {
 790:         $attributes['name'] = $name;
 791:     }
 792:     if (!isset($attributes['value'])) {
 793:         $attributes['value'] = $value;
 794:     }
 795:     return tag_open('input', $attributes, TRUE);
 796: }
 797: 
 798: /**
 799:  * &lt;input type="hidden"&gt;タグを生成する
 800:  * 
 801:  * @param   string  $data   出力するデータを表す連想配列
 802:  * @param   string  $prefix フォームの名前の接頭辞
 803:  * @return  string  生成したHTMLタグ
 804:  * @package html
 805:  */
 806: function tag_hidden($data, $prefix = NULL)
 807: {
 808:     if ($data === NULL) {
 809:         return '';
 810:     }
 811:     $str = '';
 812:     foreach ($data as $key => $value) {
 813:         if ($prefix === NULL) {
 814:             $name = $key;
 815:         } else {
 816:             $name = $prefix . '[' . $key . ']';
 817:         }
 818:         if (is_array($value)) {
 819:             $str .= tag_hidden($value, $name);
 820:         } else {
 821:             $str .= tag_input('hidden', $name, $value);
 822:         }
 823:     }
 824:     return $str;
 825: }
 826: 
 827: /**
 828:  * &lt;input type="radio"&gt;タグを生成する
 829:  * 
 830:  * @param   string  $name       name属性の値
 831:  * @param   string  $value      value属性の値
 832:  * @param   string  $label      ラベル文字列
 833:  * @param   string  $checked    選択されている場合はTRUE、そうでない場合はFALSE
 834:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
 835:  * @return  string  生成したHTMLタグ
 836:  * @package html
 837:  */
 838: function tag_radio($name, $value, $label, $checked, $attributes = NULL)
 839: {
 840:     $attributes = tag_parse_attributes($attributes);
 841:     if (!isset($attributes['type'])) {
 842:         $attributes['type'] = 'radio';
 843:     }
 844:     if (!isset($attributes['name'])) {
 845:         $attributes['name'] = $name;
 846:     }
 847:     if (!isset($attributes['value'])) {
 848:         $attributes['value'] = $value;
 849:     }
 850:     if (!isset($attributes['checked']) && $checked) {
 851:         $attributes['checked'] = 'checked';
 852:     }
 853:     if ($label === '' || $label === NULL) {
 854:         return tag_open('input', $attributes, TRUE);
 855:     } else {
 856:         $str = tag_open('label') . tag_open('input', $attributes, TRUE);
 857:         $str .= htmlspecialchars($label) . tag_close('label');
 858:         return $str;
 859:     }
 860: }
 861: 
 862: /**
 863:  * &lt;input type="checkbox"&gt;タグを生成する
 864:  * 
 865:  * @param   string  $name       name属性の値
 866:  * @param   string  $value      value属性の値
 867:  * @param   string  $label      ラベル文字列
 868:  * @param   string  $checked    選択されている場合はTRUE、そうでない場合はFALSE
 869:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
 870:  * @return  string  生成したHTMLタグ
 871:  * @package html
 872:  */
 873: function tag_checkbox($name, $value, $label, $checked, $attributes = NULL)
 874: {
 875:     $attributes = tag_parse_attributes($attributes);
 876:     if (!isset($attributes['type'])) {
 877:         $attributes['type'] = 'checkbox';
 878:     }
 879:     return tag_radio($name, $value, $label, $checked, $attributes);
 880: }
 881: 
 882: /**
 883:  * &lt;select&gt;タグを生成する
 884:  * 
 885:  * @param   string  $name       name属性の値
 886:  * @param   string  $options    選択肢の連想配列
 887:  * @param   string  $selected   選択中の値
 888:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
 889:  * @return  string  生成したHTMLタグ
 890:  * @package html
 891:  */
 892: function tag_select($name, $options, $selected, $attributes = NULL)
 893: {
 894:     $attributes = tag_parse_attributes($attributes);
 895:     if (!isset($attributes['name'])) {
 896:         $attributes['name'] = $name;
 897:     }
 898:     if (isset($attributes['blank'])) {
 899:         $blank = tag_open('option', array('value' => ''));
 900:         $blank .= htmlspecialchars($attributes['blank']);
 901:         $blank .= tag_close('option');
 902:         unset($attributes['blank']);
 903:     } else {
 904:         $blank = '';
 905:     }
 906:     $selected = strval($selected);
 907:     $str = tag_open('select', $attributes);
 908:     $str .= $blank;
 909:     foreach ($options as $value => $label) {
 910:         $option = array('value' => $value);
 911:         if (strval($value) == $selected) {
 912:             $option['selected'] = 'selected';
 913:         }
 914:         $str .= tag_open('option', $option);
 915:         $str .= htmlspecialchars($label);
 916:         $str .= tag_close('option');
 917:     }
 918:     $str .= tag_close('select');
 919:     return $str;
 920: }
 921: 
 922: /**
 923:  * &lt;textarea&gt;タグを生成する
 924:  * 
 925:  * @param   string  $name       name属性の値
 926:  * @param   string  $value      textareaに入力済みの値
 927:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
 928:  * @return  string  生成したHTMLタグ
 929:  * @package html
 930:  */
 931: function tag_textarea($name, $value, $attributes = NULL)
 932: {
 933:     $attributes = tag_parse_attributes($attributes);
 934:     if (!isset($attributes['name'])) {
 935:         $attributes['name'] = $name;
 936:     }
 937:     $str = tag_open('textarea', $attributes);
 938:     $str .= htmlspecialchars($value);
 939:     $str .= tag_close('textarea');
 940:     return $str;
 941: }
 942: 
 943: /**
 944:  * &lt;img&gt;タグを生成する
 945:  * 
 946:  * @param   string  $src        src属性の値
 947:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
 948:  * @return  string  生成したHTMLタグ
 949:  * @package html
 950:  */
 951: function tag_img($src, $attributes = NULL)
 952: {
 953:     $attributes = tag_parse_attributes($attributes);
 954:     if (!isset($attributes['src'])) {
 955:         $attributes['src'] = $src;
 956:     }
 957:     return tag_open('img', $attributes, TRUE);
 958: }
 959: 
 960: /**
 961:  * &lt;a href=""&gt;タグを生成する
 962:  * 
 963:  * @param   string  $href       href属性の値
 964:  * @param   string  $text       リンクに指定したいテキスト
 965:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
 966:  * @return  string  生成したHTMLタグ
 967:  * @package html
 968:  */
 969: function tag_a($href, $text = NULL, $attributes = NULL)
 970: {
 971:     $attributes = tag_parse_attributes($attributes);
 972:     if (!isset($attributes['href'])) {
 973:         $attributes['href'] = $href;
 974:     }
 975:     $str = tag_open('a', $attributes);
 976:     $str .= $text;
 977:     $str .= tag_close('a');
 978:     return $str;
 979: }
 980: 
 981: /**
 982:  * 段落タグを生成する
 983:  * 
 984:  * 改行コードをpタグに変換する
 985:  * 
 986:  * @param   string  $str        テキスト
 987:  * @return  string  生成したHTMLタグ
 988:  * @package html
 989:  */
 990: function tag_p($str)
 991: {
 992:     $str = trim(preg_replace("/\n\n+/", "\n", convert_newline($str)), "\n");
 993:     $str = str_replace("\n", '</p><p>', htmlspecialchars($str));
 994:     return '<p>' . $str . '</p>';
 995: }
 996: 
 997: /**
 998:  * 文字列または数値を出力する
 999:  * 
1000:  * $valueが文字列の場合には、改行コードを&lt;br /&gt;に置換、
1001:  * HTMLエスケープして出力する。
1002:  * 
1003:  * $valueが数値の場合には、カンマ区切りで出力する。
1004:  * 
1005:  * 文字幅が$widthより長い場合には、文字幅を制限して出力する。
1006:  * 
1007:  * $valueが空文字列かNULLの場合には$defaultを出力する。
1008:  * 
1009:  * @param   mixed   $value      出力する文字列または数値
1010:  * @param   integer $width      最大幅
1011:  * @param   string  $default    デフォルトで出力する値
1012:  * @package html
1013:  */
1014: function e($value, $width = 0, $default = '&nbsp;')
1015: {
1016:     if (is_int($value)) {
1017:         // 整数の出力
1018:         echo number_format($value);
1019:         return;
1020:     }
1021: 
1022:     $value = strval($value);
1023:     if ($value === '') {
1024:         // 空文字列の出力
1025:         echo $default;
1026:         return;
1027:     }
1028: 
1029:     // 文字幅が設定された場合
1030:     if ($width != 0) {
1031:         if (mb_strwidth($value) > $width) {
1032:             $value = mb_strimwidth($value, 0, $width) . '..';
1033:         }
1034:     }
1035: 
1036:     // 文字列の出力
1037:     echo nl2br(htmlspecialchars($value));
1038: }
1039: 
1040: //------------------------------------------------------------------------------
1041: // フォーム
1042: //------------------------------------------------------------------------------
1043: 
1044: /**
1045:  * フォームの入力値を設定する
1046:  * 
1047:  * @param   mixed   $name   対象のフォームの名前、全て設定する場合にはNULL
1048:  * @param   mixed   $value  フォームの入力値
1049:  * @package form
1050:  */
1051: function form_set_value($name, $value)
1052: {
1053:     global $__photon_form_value;
1054:     if (!isset($__photon_form_value)) {
1055:         $__photon_form_value = array();
1056:     }
1057:     array_set($__photon_form_value, $name, $value);
1058: }
1059: 
1060: /**
1061:  * フォームの入力値を取得する
1062:  * 
1063:  * @param   mixed   $name   対象のフォームの名前、全て取得する場合にはNULL
1064:  * @return  mixed   フォームの入力値
1065:  * @package form
1066:  */
1067: function form_get_value($name)
1068: {
1069:     global $__photon_form_value;
1070:     if (!isset($__photon_form_value)) {
1071:         return NULL;
1072:     }
1073:     return array_get($__photon_form_value, $name);
1074: }
1075: 
1076: /**
1077:  * フォームの入力値に指定した値が含まれているか調べる
1078:  * 
1079:  * @param   mixed   $name   対象のフォームの名前、全て設定する場合にはNULL
1080:  * @param   string  $check  含まれているか調べる値
1081:  * @return  boolean 入力値に指定した値が含まれている場合にはTRUE、
1082:  *                  含まれていない場合にはFALSE
1083:  * @package form
1084:  */
1085: function form_check_value($name, $check)
1086: {
1087:     $check = strval($check);
1088:     $value = form_get_value($name);
1089:     if (is_array($value)) {
1090:         return in_array($check, $value);
1091:     } else {
1092:         return $check === strval($value);
1093:     }
1094: }
1095: 
1096: /**
1097:  * フォームの選択肢を設定する
1098:  * 
1099:  * @param   string  $name       選択肢の名前
1100:  * @param   array   $options    選択肢の連想配列
1101:  * @package form
1102:  */
1103: function form_set_options($name, $options)
1104: {
1105:     global $__photon_form_options;
1106:     $__photon_form_options[$name] = $options;
1107: }
1108: 
1109: /**
1110:  * フォームの選択肢を取得する
1111:  * 
1112:  * form_set_optionsで選択肢が設定されている場合には、その値を返す。
1113:  * また、(選択肢の名前)_get_optionsという名前の関数が存在する場合には、
1114:  * その返り値を選択肢として返す。
1115:  * 
1116:  * @param   string  $name       選択肢の名前
1117:  * @return  array   選択肢の連想配列
1118:  * @package form
1119:  */
1120: function form_get_options($name)
1121: {
1122:     global $__photon_form_options;
1123:     if (is_array($name)) {
1124:         return $name;
1125:     }
1126:     if (isset($__photon_form_options[$name])) {
1127:         return $__photon_form_options[$name];
1128:     }
1129:     $function = $name . '_get_options';
1130:     if (function_exists($function)) {
1131:         return call_user_func($function);
1132:     }
1133:     $function = 'get_' . $name . '_options';
1134:     if (function_exists($function)) {
1135:         return call_user_func($function);
1136:     }
1137:     return array();
1138: }
1139: 
1140: /**
1141:  * フォームを読込専用に設定する
1142:  * 
1143:  * $nameをNULLに設定した場合、個別に設定しないフォームの状態を設定する。
1144:  * 
1145:  * @param   mixed   $name       対象のフォームの名前、全て設定する場合にはNULL
1146:  * @param   boolean $is_static  読込専用ならTRUE、そうでない場合にはFALSE
1147:  * @package form
1148:  */
1149: function form_set_static($name = NULL, $is_static = TRUE)
1150: {
1151:     global $__photon_form_static;
1152:     global $__photon_form_static_default;
1153:     if ($name !== NULL) {
1154:         $__photon_form_static[$name] = $is_static;
1155:     } else {
1156:         $__photon_form_static_default = $is_static;
1157:     }
1158: }
1159: 
1160: /**
1161:  * フォームが読込専用か調べる
1162:  * 
1163:  * @param   mixed   $name       対象のフォームの名前
1164:  * @return  boolean $is_static  読込専用ならTRUE、そうでない場合にはFALSE
1165:  * @package form
1166:  */
1167: function form_get_static($name)
1168: {
1169:     global $__photon_form_static;
1170:     global $__photon_form_static_default;
1171:     if (isset($__photon_form_static[$name])) {
1172:         return $__photon_form_static[$name];
1173:     } else {
1174:         return $__photon_form_static_default;
1175:     }
1176: }
1177: 
1178: /**
1179:  * フォームの開始タグを生成する
1180:  * 
1181:  * @param   string  $action     action属性の値
1182:  * @param   string  $method     method属性の値
1183:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
1184:  * @return  string  生成したHTMLタグ
1185:  * @package form
1186:  */
1187: function form_open($action, $method = 'post', $attributes = NULL)
1188: {
1189:     if (!isset($attributes['action'])) {
1190:         $attributes['action'] = $action;
1191:     }
1192:     if (!isset($attributes['method'])) {
1193:         $attributes['method'] = $method;
1194:     }
1195:     return tag_open('form', $attributes);
1196: }
1197: 
1198: /**
1199:  * アップロードを伴うフォームの開始タグを生成する
1200:  * 
1201:  * @param   string  $action     action属性の値
1202:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
1203:  * @return  string  生成したHTMLタグ
1204:  * @package form
1205:  */
1206: function form_open_multipart($action, $attributes = NULL)
1207: {
1208:     if (!isset($attributes['enctype'])) {
1209:         $attributes['enctype'] = 'multipart/form-data';
1210:     }
1211:     return form_open($action, 'post', $attributes);
1212: }
1213: 
1214: /**
1215:  * フォームの終了タグを生成する
1216:  * 
1217:  * @return  string  生成したHTMLタグ
1218:  * @package form
1219:  */
1220: function form_close()
1221: {
1222:     return tag_close('form');
1223: }
1224: 
1225: /**
1226:  * 読込専用のテキスト入力フォームを生成する
1227:  * 
1228:  * 下記のように入力値と表示する値が同じ場合で使用する。
1229:  * - form_text
1230:  * - form_textarea
1231:  * - form_radio_array
1232:  * - form_checkbox_array
1233:  * - form_select_array
1234:  * - form_select_number
1235:  * 
1236:  * 入力値が単一の値の場合には、HTMLエスケープ、改行コードを&lt;br /&gt;に
1237:  * 変換して返す。
1238:  * 
1239:  * config関数で設定した下記の設定オプションが使用される。
1240:  * 
1241:  * |* static_glue   | 入力値が配列の場合に結合する文字列
1242:  * 
1243:  * @param   mixed   $name   対象のフォームの名前
1244:  * @return  string  生成したHTMLタグ
1245:  * @package form
1246:  */
1247: function form_static($name)
1248: {
1249:     $value = form_get_value($name);
1250:     if (is_array($value)) {
1251:         $value = implode(config('static_glue'), $value);
1252:     }
1253:     return nl2br(htmlspecialchars($value));
1254: }
1255: 
1256: /**
1257:  * 読込専用の選択肢フォームを生成する
1258:  * 
1259:  * 下記のように連想配列から選択する場合で使用する。
1260:  * - form_radio_assoc
1261:  * - form_checkbox_assoc
1262:  * - form_select_assoc
1263:  * 
1264:  * 入力値が単一の値の場合には、連想配列でルックアップして返す。
1265:  * 
1266:  * config関数で設定した下記の設定オプションが使用される。
1267:  * 
1268:  * |* static_glue   | 入力値が配列の場合に結合する文字列
1269:  * 
1270:  * @param   mixed   $name       対象のフォームの名前
1271:  * @param   mixed   $options    選択肢の連想配列、または選択肢の名前
1272:  * @return  string  生成したHTMLタグ
1273:  * @package form
1274:  */
1275: function form_static_assoc($name, $options)
1276: {
1277:     $value = form_get_value($name);
1278:     $options = form_get_options($options);
1279:     if (is_array($value)) {
1280:         $array = array();
1281:         foreach ($value as $v) {
1282:             $array[] = $options[$v];
1283:         }
1284:         $value = implode(config('static_glue'), $array);
1285:     } else {
1286:         $value = $options[$value];
1287:     }
1288:     return nl2br(htmlspecialchars($value));
1289: }
1290: 
1291: /**
1292:  * 読込専用のパスワード入力フォームを生成する
1293:  * 
1294:  * form_passwordのように入力値を出力できない場合で使用する。
1295:  * 隠し文字を入力値の文字数だけ繰り返す。
1296:  * 
1297:  * config関数で設定した下記の設定オプションが使用される。
1298:  * 
1299:  * |* static_hidden | パスワードの隠し文字
1300:  * 
1301:  * @param   mixed   $name   対象のフォームの名前
1302:  * @return  string  生成したHTMLタグ
1303:  * @package form
1304:  */
1305: function form_static_password($name)
1306: {
1307:     $value = form_get_value($name);
1308:     return str_repeat(config('static_hidden'), strlen($value));
1309: }
1310: 
1311: /**
1312:  * 読込専用の真偽値フォームを生成する
1313:  * 
1314:  * 下記のように真偽値を選択する場合で使用する。
1315:  * - form_radio
1316:  * - form_checkbox
1317:  * 
1318:  * config関数で設定した下記の設定オプションが使用される。
1319:  * 
1320:  * |* static_true   | 真の値の場合の文字列
1321:  * |* static_false  | 偽の値の場合の文字列
1322:  * 
1323:  * @param   mixed   $name   対象のフォームの名前
1324:  * @param   mixed   $check  選択時の値
1325:  * @return  string  生成したHTMLタグ
1326:  * @package form
1327:  */
1328: function form_static_boolean($name, $check)
1329: {
1330:     if (form_check_value($name, $check)) {
1331:         return config('static_true');
1332:     } else {
1333:         return config('static_false');
1334:     }
1335: }
1336: 
1337: /**
1338:  * 単一行テキスト入力フォームを生成する
1339:  * 
1340:  * @param   string  $name   対象のフォームの名前
1341:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
1342:  * @return  string  生成したHTMLタグ
1343:  * @package form
1344:  */
1345: function form_text($name, $attributes = NULL)
1346: {
1347:     if (form_get_static($name)) {
1348:         return form_static($name);
1349:     } else {
1350:         return tag_input('text', $name, form_get_value($name), $attributes);
1351:     }
1352: }
1353: 
1354: /**
1355:  * パスワード入力フォームを生成する
1356:  * 
1357:  * @param   string  $name   対象のフォームの名前
1358:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
1359:  * @return  string  生成したHTMLタグ
1360:  * @package form
1361:  */
1362: function form_password($name, $attributes = NULL)
1363: {
1364:     if (form_get_static($name)) {
1365:         return form_static_password($name);
1366:     } else {
1367:         return tag_input('password', $name, form_get_value($name), $attributes);
1368:     }
1369: }
1370: 
1371: /**
1372:  * ファイル入力フォームを生成する
1373:  * 
1374:  * @param   string  $name   対象のフォームの名前
1375:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
1376:  * @return  string  生成したHTMLタグ
1377:  * @package form
1378:  */
1379: function form_file($name, $attributes = NULL)
1380: {
1381:     if (form_get_static($name)) {
1382:         return form_static($name);
1383:     } else {
1384:         return form_hidden($name) . tag_input('file', $name, '', $attributes);
1385:     }
1386: }
1387: 
1388: /**
1389:  * 隠しフォームを生成する
1390:  * 
1391:  * @param   mixed   $names  対象のフォームの名前、またはフォームの名前の連想配列
1392:  * @return  string  生成したHTMLタグ
1393:  * @package form
1394:  */
1395: function form_hidden($names)
1396: {
1397:     if (func_num_args() != 1) {
1398:         return form_hidden(func_get_args());
1399:     } elseif (is_array($names)) {
1400:         $str = '';
1401:         foreach ($names as $name) {
1402:             $str .= tag_hidden(array($name => form_get_value($name)));
1403:         }
1404:         return $str;
1405:     } else if ($names === NULL) {
1406:         return tag_hidden(form_get_value(NULL));
1407:     } else {
1408:         return form_hidden(array($names));
1409:     }
1410: }
1411: 
1412: /**
1413:  * 複数行テキスト入力フォームを生成する
1414:  * 
1415:  * @param   string  $name   対象のフォームの名前
1416:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
1417:  * @return  string  生成したHTMLタグ
1418:  * @package form
1419:  */
1420: function form_textarea($name, $attributes = NULL)
1421: {
1422:     if (form_get_static($name)) {
1423:         return form_static($name);
1424:     } else {
1425:         return tag_textarea($name, form_get_value($name), $attributes);
1426:     }
1427: }
1428: 
1429: /**
1430:  * ラジオボタンを生成する
1431:  * 
1432:  * $labelにNULL以外の値を指定した場合、&lt;label&gt;タグによるラベルも生成する。
1433:  * 
1434:  * @param   string  $name       対象のフォームの名前
1435:  * @param   string  $value      選択時の値
1436:  * @param   string  $label      ラベルの文字列
1437:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
1438:  * @return  string  生成したHTMLタグ
1439:  * @package form
1440:  */
1441: function form_radio($name, $value, $label = NULL, $attributes = NULL)
1442: {
1443:     if (form_get_static($name)) {
1444:         return form_static_boolean($name, $value);
1445:     } else {
1446:         $checked = form_check_value($name, $value);
1447:         return tag_radio($name, $value, $label, $checked, $attributes);
1448:     }
1449: }
1450: 
1451: /**
1452:  * 連想配列から選択するラジオボタンを生成する
1453:  * 
1454:  * $optionsは、選択時の値=>選択肢の文字列とした連想配列を指定する。
1455:  * form_set_optionsで設定した選択肢の名前を指定することもできる。
1456:  * 
1457:  * @param   string  $name       対象のフォームの名前
1458:  * @param   array   $options    選択肢の連想配列、または選択肢の名前
1459:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
1460:  * @return  string  生成したHTMLタグ
1461:  * @package form
1462:  */
1463: function form_radio_assoc($name, $options, $attributes = NULL)
1464: {
1465:     if (form_get_static($name)) {
1466:         return form_static_assoc($name, $options);
1467:     } else {
1468:         $options = form_get_options($options);
1469:         $html = '';
1470:         foreach ($options as $value => $label) {
1471:             $checked = form_check_value($name, $value);
1472:             $html .= tag_radio($name, $value, $label, $checked, $attributes);
1473:         }
1474:         return $html;
1475:     }
1476: }
1477: 
1478: /**
1479:  * 配列から選択するラジオボタンを生成する
1480:  * 
1481:  * $optionsは、選択時の値・文字列の配列とする。
1482:  * 
1483:  * @param   string  $name       対象のフォームの名前
1484:  * @param   array   $array      選択肢の配列
1485:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
1486:  * @return  string  生成したHTMLタグ
1487:  * @package form
1488:  */
1489: function form_radio_array($name, $array, $attributes = NULL)
1490: {
1491:     if (form_get_static($name)) {
1492:         return form_static($name);
1493:     } else {
1494:         $html = '';
1495:         foreach ($array as $value) {
1496:             $checked = form_check_value($name, $value);
1497:             $html .= tag_radio($name, $value, $value, $checked, $attributes);
1498:         }
1499:         return $html;
1500:     }
1501: }
1502: 
1503: /**
1504:  * チェックボックスを生成する
1505:  * 
1506:  * $labelにNULL以外の値を指定した場合、&lt;label&gt;タグによるラベルも生成する。
1507:  * 
1508:  * @param   string  $name       対象のフォームの名前
1509:  * @param   string  $value      選択時の値
1510:  * @param   string  $label      ラベルの文字列
1511:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
1512:  * @return  string  生成したHTMLタグ
1513:  * @package form
1514:  */
1515: function form_checkbox($name, $value, $label, $attributes = NULL)
1516: {
1517:     if (form_get_static($name)) {
1518:         return form_static_boolean($name, $value);
1519:     } else {
1520:         $checked = form_check_value($name, $value);
1521:         return tag_checkbox($name, $value, $label, $checked, $attributes);
1522:     }
1523: }
1524: 
1525: /**
1526:  * 連想配列から選択するチェックボックスを生成する
1527:  * 
1528:  * $optionsは、選択時の値=>選択肢の文字列とした連想配列を指定する。
1529:  * form_set_optionsで設定した選択肢の名前を指定することもできる。
1530:  * 
1531:  * $nameの終端が[]でない場合、[]を付け加える。
1532:  * 
1533:  * @param   string  $name       対象のフォームの名前
1534:  * @param   array   $options    選択肢の連想配列、または選択肢の名前
1535:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
1536:  * @return  string  生成したHTMLタグ
1537:  * @package form
1538:  */
1539: function form_checkbox_assoc($name, $options, $attributes = NULL)
1540: {
1541:     if (form_get_static($name)) {
1542:         return form_static_assoc($name, $options);
1543:     } else {
1544:         if (substr($name, -2) != '[]') {
1545:             $name .= '[]';
1546:         }
1547:         $options = form_get_options($options);
1548:         $html = '';
1549:         foreach ($options as $value => $label) {
1550:             $checked = form_check_value($name, $value);
1551:             $html .= tag_checkbox($name, $value, $label, $checked, $attributes);
1552:         }
1553:         return $html;
1554:     }
1555: }
1556: 
1557: /**
1558:  * 配列から選択するチェックボックスを生成する
1559:  * 
1560:  * $optionsは、選択時の値・文字列の配列とする。
1561:  * 
1562:  * $nameの終端が[]でない場合、[]を付け加える。
1563:  * 
1564:  * @param   string  $name       対象のフォームの名前
1565:  * @param   array   $array      選択肢の配列
1566:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
1567:  * @return  string  生成したHTMLタグ
1568:  * @package form
1569:  */
1570: function form_checkbox_array($name, $array, $attributes = NULL)
1571: {
1572:     if (form_get_static($name)) {
1573:         return form_static($name);
1574:     } else {
1575:         if (substr($name, -2) != '[]') {
1576:             $name .= '[]';
1577:         }
1578:         $html = '';
1579:         foreach ($array as $value) {
1580:             $checked = form_check_value($name, $value);
1581:             $html .= tag_checkbox($name, $value, $value, $checked, $attributes);
1582:         }
1583:         return $html;
1584:     }
1585: }
1586: 
1587: /**
1588:  * 連想配列から選択するドロップダウンを生成する
1589:  * 
1590:  * $optionsは、選択時の値=>選択肢の文字列とした連想配列を指定する。
1591:  * form_set_optionsで設定した選択肢の名前を指定することもできる。
1592:  * 
1593:  * @param   string  $name       対象のフォームの名前
1594:  * @param   array   $options    選択肢の連想配列、または選択肢の名前
1595:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
1596:  * @return  string  生成したHTMLタグ
1597:  * @package form
1598:  */
1599: function form_select_assoc($name, $options, $attributes = NULL)
1600: {
1601:     if (form_get_static($name)) {
1602:         return form_static_assoc($name, $options);
1603:     } else {
1604:         $options = form_get_options($options);
1605:         $selected = form_get_value($name);
1606:         return tag_select($name, $options, $selected, $attributes);
1607:     }
1608: }
1609: 
1610: /**
1611:  * 配列から選択するドロップダウンを生成する
1612:  * 
1613:  * $optionsは、選択時の値・文字列の配列とする。
1614:  * 
1615:  * @param   string  $name       対象のフォームの名前
1616:  * @param   array   $array      選択肢の配列
1617:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
1618:  * @return  string  生成したHTMLタグ
1619:  * @package form
1620:  */
1621: function form_select_array($name, $array, $attributes = NULL)
1622: {
1623:     if (form_get_static($name)) {
1624:         return form_static_assoc($name, $options);
1625:     } else {
1626:         $options = array();
1627:         foreach ($array as $value) {
1628:             $options[$value] = $value;
1629:         }
1630:         $selected = form_get_value($name);
1631:         return tag_select($name, $options, $selected, $attributes);
1632:     }
1633: }
1634: 
1635: /**
1636:  * 数値から選択するドロップダウンを生成する
1637:  * 
1638:  * @param   string  $name       対象のフォームの名前
1639:  * @param   array   $min        選択肢の下限値
1640:  * @param   array   $max        選択肢の上限値
1641:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
1642:  * @return  string  生成したHTMLタグ
1643:  * @package form
1644:  */
1645: function form_select_number($name, $min, $max, $attributes = NULL)
1646: {
1647:     if (form_get_static($name)) {
1648:         return form_static_assoc($name, $options);
1649:     } else {
1650:         $options = array();
1651:         for ($value = $min; $value <= $max; $value++) {
1652:             $options[$value] = $value;
1653:         }
1654:         $selected = form_get_value($name);
1655:         return tag_select($name, $options, $selected, $attributes);
1656:     }
1657: }
1658: 
1659: /**
1660:  * ファイルのアップロードフォームを生成する
1661:  * 
1662:  * config関数で設定した下記の設定オプションが使用される。
1663:  * 
1664:  * |* form_upload_remove    | 削除のチェックボックスのラベル
1665:  * |* form_upload_url       | アップロード済みの基準URL
1666:  * |* form_upload_link      | アップロード済みのテキスト
1667:  * 
1668:  * @param   string  $name       対象のフォームの名前
1669:  * @param   boolean $remove     削除できる場合にはTRUE、そうでない場合にはFALSE
1670:  * @param   boolean $link       アップロード済みのファイルへのリンクを
1671:  *                              設置する場合にはTRUE、そうでない場合にはFALSE
1672:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
1673:  * @return  string  生成したHTMLタグ
1674:  * @see     form_upload_file
1675:  * @package form
1676:  */
1677: function form_upload($name, $remove = TRUE, $link = TRUE, $attributes = NULL)
1678: {
1679:     $html = form_file($name, $attributes);
1680:     $value = form_get_value($name);
1681:     if ($value === '') {
1682:         return $html;
1683:     }
1684:     if ($remove) {
1685:         $remove_name = '__remove_' . $name;
1686:         $html .= form_checkbox($remove_name, 'y', config('form_upload_remove'));
1687:     }
1688:     if ($link) {
1689:         $url = config('form_upload_url') . form_get_value($name);
1690:         $ext = extension($value);
1691:         if (in_array($ext, array('jpg', 'jpeg', 'gif', 'png'))) {
1692:             $text = tag_img($value);
1693:         } else {
1694:             $text = config('form_upload_link');
1695:         }
1696:         $html .= tag_a($url, $text);
1697:     }
1698:     return $html;
1699: }
1700: 
1701: /**
1702:  * 日時の選択肢フォームを生成する
1703:  * 
1704:  * 日時選択のフォーマットには、下記の書式文字列を使用できる。
1705:  * 
1706:  * |* {y}   |近年の選択肢 (2010年〜2100年)
1707:  * |* {p}   |過去の年の選択肢 (1900年〜今年)
1708:  * |* {m}   |月の選択肢
1709:  * |* {d}   |日の選択肢
1710:  * |* {h}   |時の選択肢
1711:  * |* {i}   |分の選択肢
1712:  * |* {s}   |秒の選択肢
1713:  * 
1714:  * デフォルトでは{y}/{m}/{d}が使用される。
1715:  * 
1716:  * config関数で設定した下記の設定オプションが使用される。
1717:  * 
1718:  * |* form_date_year_min    | {y}の選択肢の最小値
1719:  * |* form_date_year_max    | {y}の選択肢の最大値
1720:  * |* form_date_past_min    | {p}の選択肢の最小値
1721:  * 
1722:  * 選択された値は、y, m, d, h, i, sをキーとする連想配列となるので、
1723:  * MySQLデータベースに格納する際には、form_date_convertで変換する。
1724:  * 
1725:  * フォームの入力値は、MySQLデータベースの形式YYYY-MM-DD HH:II:SSか、
1726:  * y, m, d, h, i, sをキーとする連想配列に対応する。
1727:  * 
1728:  * @param   string  $name       対象のフォームの名前
1729:  * @param   boolean $format     日時選択のフォーマット
1730:  * @param   mixed   $attributes 追加するタグの属性の文字列、または連想配列
1731:  * @return  string  生成したHTMLタグ
1732:  * @see     form_date_convert
1733:  * @package form
1734:  */
1735: function form_date($name, $format = NULL, $attributes = NULL)
1736: {
1737:     $value = form_get_value($name);
1738: 
1739:     // MySQLデータベースの形式から配列形式に変換
1740:     if (is_string($value)) {
1741:         $array = sscanf($value, '%04d-%02d-%02d %02d:%02d:%02d');
1742:         form_set_value($name . '[y]', intval($array[0]));
1743:         form_set_value($name . '[m]', intval($array[1]));
1744:         form_set_value($name . '[d]', intval($array[2]));
1745:         form_set_value($name . '[h]', intval($array[3]));
1746:         form_set_value($name . '[i]', intval($array[4]));
1747:         form_set_value($name . '[s]', intval($array[5]));
1748:     }
1749: 
1750:     // 日時のドロップダウンを生成
1751:     $vars = array();
1752:     $y_min = config('form_date_year_min');
1753:     $y_max = config('form_date_year_max');
1754:     $p_min = config('form_date_past_min');
1755:     $p_max = intval(date('Y'));
1756:     $vars['y'] = form_select_number($name . '[y]', $y_min, $y_max, $attributes);
1757:     $vars['p'] = form_select_number($name . '[y]', $p_min, $p_max, $attributes);
1758:     $vars['m'] = form_select_number($name . '[m]', 1, 12, $attributes);
1759:     $vars['d'] = form_select_number($name . '[d]', 1, 31, $attributes);
1760:     $vars['h'] = form_select_number($name . '[h]', 0, 23, $attributes);
1761:     $vars['i'] = form_select_number($name . '[i]', 0, 59, $attributes);
1762:     $vars['s'] = form_select_number($name . '[s]', 0, 59, $attributes);
1763: 
1764:     // フォーマットに合わせて選択肢のHTMLを生成
1765:     if ($format === NULL) {
1766:         $format = config('form_date_format');
1767:     }
1768:     return embed($format, $vars);
1769: }
1770: 
1771: /**
1772:  * アップロードされたファイルの情報を取得する
1773:  * 
1774:  * フォームの名前がa[b][c]のように配列の形式にも対応している。
1775:  * 
1776:  * @param   string  $name   対象のフォームの名前
1777:  * @param   string  $key    $_FILESのキーの名前
1778:  * @return  string  アップロードされたファイルの情報
1779:  * @see     form_upload
1780:  * @package form
1781:  */
1782: function form_get_file($name, $key)
1783: {
1784:     // 名前の先頭と先頭以降に分ける
1785:     $keys = explode('.', str_replace(array('][', '[', ']'), '.', $name));
1786:     $head = array_shift($keys);
1787: 
1788:     // $_FILESの値を取得する
1789:     return array_get($_FILES[$head][$key], $keys);
1790: }
1791: 
1792: /**
1793:  * アップロードされたファイルを保存する
1794:  * 
1795:  * アップロードされたファイルは、一意の名前を付け、$dirに保存する。
1796:  * $dataの該当値には、$dirと保存した名前を結合した文字列を設定する。
1797:  * 
1798:  * form_uploadで削除フラグを選択した場合には、
1799:  * $dataの該当値を空文字列に設定する。
1800:  * 
1801:  * アップロードした拡張子が$extと異なる場合には、エラーとなる。
1802:  * $extにNULLを設定した場合には、全ての拡張子のファイルが受け入れられる。
1803:  * 
1804:  * config関数で設定した下記の設定オプションが使用される。
1805:  * 
1806:  * |* form_upload_dir   | アップロード先のディレクトリ
1807:  * |* error_upload      | アップロードに失敗した場合のエラーメッセージ
1808:  * 
1809:  * @param   array   $data   入力データの連想配列
1810:  * @param   string  $name   対象のフォームの名前
1811:  * @param   string  $dir    アップロード先のディレクトリ
1812:  * @param   mixed   $extensions 許可される拡張子の連想配列、カンマ区切りの文字列
1813:  * @see     form_upload
1814:  * @package form
1815:  */
1816: function form_upload_file(&$data, $name, $dir = NULL, $extensions = NULL)
1817: {
1818:     $error = form_get_file($name, 'error');
1819:     if ($error == UPLOAD_ERR_OK) {
1820:         // 元のファイルの拡張子を取得し、チェックする
1821:         $ext = extension(form_get_file($name, 'name'));
1822:         if ($extensions !== NULL) {
1823:             if (!is_array($extensions)) {
1824:                 $extensions = explode(',', $extensions);
1825:             }
1826:             if (!in_array($ext, $extensions)) {
1827:                 return form_set_error($name, config('error_upload'));
1828:             }
1829:         }
1830: 
1831:         // ファイルの置き場所
1832:         $tmp_name = form_get_file($name, 'tmp_name');
1833: 
1834:         // アップロード先のディレクトリを求める
1835:         $dir = rtrim($dir, '/');
1836:         $base_dir = rtrim(config('form_upload_dir'), '/');
1837:         $dest_dir = $base_dir . '/' . $dir . '/';
1838: 
1839:         // アップロード先のディレクトリを生成する
1840:         if (!file_exists($dest_dir)) {
1841:             mkdir($dest_dir, 0777, TRUE);
1842:         }
1843: 
1844:         // アップロード先のファイル名
1845:         $filename = md5_file($tmp_name) . '.' . $src_ext;
1846:         $dest_path = $dest_dir . $filename;
1847:         $value = $dir . '/' . $filename;
1848: 
1849:         if (file_exists($dest_path)) {
1850:             // 同一のファイルが存在している場合
1851:             unlink($tmp_name);
1852:             return array_set($data, $name, $value);
1853:         } else if (move_uploaded_file($tmp_name, $dest_path)) {
1854:             // ファイルのアップロードに成功
1855:             return array_set($data, $name, $value);
1856:         } else {
1857:             // アップロードに失敗
1858:             return form_set_error($name, config('error_upload'));
1859:         }
1860:     } elseif ($error != UPLOAD_ERR_NO_FILE) {
1861:         // エラー
1862:         return form_set_error($name, config('error_upload'));
1863:     }
1864: 
1865:     // ファイル削除
1866:     $remove_name = '__remove_' . $name;
1867:     if (array_get($_REQUEST, $remove_name) == 'y') {
1868:         array_set($data, $name, '');
1869:     }
1870: }
1871: 
1872: /**
1873:  * form_dateで入力された日時をMySQLデータベース形式に変換する
1874:  * 
1875:  * $dataの該当値がy, m, d, h, i, sをキーとした連想配列である場合、
1876:  * yyyy-mm-dd hh:ii:ss形式による文字列を設定する。
1877:  * 
1878:  * @param   array   $data   入力データの連想配列
1879:  * @param   string  $name   対象のフォームの名前
1880:  * @see     form_date
1881:  * @package form
1882:  */
1883: function form_date_convert(&$data, $name)
1884: {
1885:     $value = array_get($data, $name);
1886:     if (is_array($value)) {
1887:         $args = array();
1888:         $y = intval($value['y']);
1889:         $m = intval($value['m']);
1890:         $d = intval($value['d']);
1891:         $str = sprintf('%04d-%02d-%02d', $y, $m, $d);
1892:         if (isset($value['h'])) {
1893:             $h = intval($value['h']);
1894:             $i = intval($value['i']);
1895:             $s = intval($value['s']);
1896:             $str .= sprintf(' %02d:%02d:%02d', $h, $i, $s);
1897:         }
1898:         array_set($data, $name, $str);
1899:     }
1900: }
1901: 
1902: //------------------------------------------------------------------------------
1903: // フィルタとバリデーション
1904: //------------------------------------------------------------------------------
1905: 
1906: /**
1907:  * フォームのエラーを設定する
1908:  * 
1909:  * @param   string  $name       対象のフォームの名前
1910:  * @param   string  $message    エラーメッセージの文字列
1911:  * @param   array   $vars       エラーメッセージの変数に埋め込む連想配列
1912:  * @package validate
1913:  */
1914: function form_set_error($name, $message, $vars)
1915: {
1916:     global $__photon_form_error;
1917:     $__photon_form_error[$name] = embed($message, $vars);
1918: }
1919: 
1920: /**
1921:  * フォームのエラーを取得する
1922:  * 
1923:  * @param   string  $name       対象のフォームの名前
1924:  * @return  string  エラーメッセージの文字列、エラーが発生していない場合にはNULL
1925:  * @package validate
1926:  */
1927: function form_get_error($name)
1928: {
1929:     global $__photon_form_error;
1930:     if (isset($__photon_form_error[$name])) {
1931:         return $__photon_form_error[$name];
1932:     } else {
1933:         return NULL;
1934:     }
1935: }
1936: 
1937: /**
1938:  * フォームにエラーが発生しているかを調べる
1939:  * 
1940:  * @return  boolean エラーが発生している場合にはTRUE、そうでない場合にはFALSE
1941:  * @package validate
1942:  */
1943: function form_has_error()
1944: {
1945:     global $__photon_form_error;
1946:     return count($__photon_form_error) > 0 ? TRUE : FALSE;
1947: }
1948: 
1949: /**
1950:  * フォームのエラーをHTMLで取得する
1951:  * 
1952:  * 複数引数が指定された場合、エラーが発生している、
1953:  * 先頭の引数のフォームのエラーメッセージを取得する。
1954:  * 
1955:  * config関数で設定した下記の設定オプションが使用される。
1956:  * 
1957:  * |* error_tag_open    | エラーメッセージの開始タグ
1958:  * |* error_tag_close   | エラーメッセージの終了タグ
1959:  * 
1960:  * @param   string  $name   対象のフォームの名前
1961:  * @return  string  エラーメッセージのHTML、エラーが発生していない場合にはNULL
1962:  * @package validate
1963:  */
1964: function form_error($name)
1965: {
1966:     foreach (func_get_args() as $name) {
1967:         $message = form_get_error($name);
1968:         if ($message !== NULL) {
1969:             $html = config('error_tag_open');
1970:             $html .= htmlspecialchars($message);
1971:             $html .= config('error_tag_close');
1972:             return $html;
1973:         }
1974:     }
1975:     return NULL;
1976: }
1977: 
1978: /**
1979:  * フィルタ・バリデーションルールを設定する
1980:  * 
1981:  * @param   string  $name   対象のフォームの名前
1982:  * @param   array   $rule   ルールの連想配列
1983:  * @package validate
1984:  */
1985: function rule($name, $rule)
1986: {
1987:     global $__photon_form_rule;
1988:     $__photon_form_rule[$name] = $rule;
1989: }
1990: 
1991: /**
1992:  * 入力データのフィルタ処理を行う
1993:  * 
1994:  * rule関数で下記の項目について設定ができる。
1995:  * 
1996:  * |* kana |mb_convert_kanaの引数、変換しない場合はNULL。
1997:  * |* trim |トリミングするならy。デフォルトはy
1998:  * |* case |大文字にするならupper、小文字にするならlower、変換しない場合はNULL
1999:  * 
2000:  * @param   array   $data   フィルタ処理を行う連想配列
2001:  * @return  array   フィルタ処理の結果の連想配列
2002:  * @package validate
2003:  */
2004: function filter($data)
2005: {
2006:     global $__photon_form_rule;
2007:     $return = array();
2008:     foreach ($__photon_form_rule as $name => $rule) {
2009:         $value = array_get($data, $name);
2010:         $value = __filter($value, $rule);
2011:         array_set($return, $name, $value);
2012:     }
2013:     return $return;
2014: }
2015: 
2016: /** @ignore */
2017: function __filter($value, $rule)
2018: {
2019:     // 配列の場合の処理
2020:     if (is_array($value)) {
2021:         foreach ($value as $k => $v) {
2022:             $value[$k] = __filter($v, $rule);
2023:         }
2024:         return $value;
2025:     }
2026: 
2027:     // 多バイト文字の変換
2028:     $kana = isset($rule['kana']) ? $rule['kana'] : 'saKV';
2029:     if ($kana !== '') {
2030:         $value = mb_convert_kana($value, $kana);
2031:     }
2032: 
2033:     // 両端の空白を除去する
2034:     $trim = isset($rule['trim']) ? $rule['trim'] : 'y';
2035:     if (is_true($trim)) {
2036:         $value = trim($value);
2037:     }
2038: 
2039:     // 大文字・小文字の変換
2040:     $case = isset($rule['case']) ? $rule['case'] : '';
2041:     if ($case == 'upper') {
2042:         $value = strtoupper($value);
2043:     } elseif ($case == 'lower') {
2044:         $value = strtolower($value);
2045:     }
2046: 
2047:     // 改行コードを変換する
2048:     $value = convert_newline($value);
2049: 
2050:     return $value;
2051: }
2052: 
2053: /**
2054:  * 入力データのバリデーション処理を行う
2055:  * 
2056:  * rule関数で下記の項目について設定ができる。
2057:  * 
2058:  * |* required  | 入力必須項目ならy
2059:  * |* min_bytes | 最小バイト数
2060:  * |* max_bytes | 最大バイト数
2061:  * |* min_chars | 最小文字数
2062:  * |* max_chars | 最大文字数
2063:  * |* min_width | 最小文字幅
2064:  * |* max_width | 最大文字幅
2065:  * |* min_lines | 最小行数
2066:  * |* max_lines | 最大行数
2067:  * |* min_value | 最小入力値
2068:  * |* max_value | 最大入力値
2069:  * |* matches   | 一致すべき他の入力フォームの名前
2070:  * |* options   | 選択肢の連想配列、名前の文字列
2071:  * |* preg      | Perl正規表現形式による書式
2072:  * |* type      | alpha     | 英字
2073:  * |^           | digit     | 数字
2074:  * |^           | alnum     | 英数字
2075:  * |^           | alnum_dash| 英数字、アンダーバー("_")、ダッシュ("-")
2076:  * |^           | graph     | 空白を除く表示可能な文字列
2077:  * |^           | integer   | 整数 (マイナスを含む)
2078:  * |^           | natural   | 自然数 (0以上の整数)
2079:  * |^           | decimal   | 小数値を含む数値
2080:  * |^           | mail      | メールアドレス
2081:  * |^           | ipv4      | IPv4アドレス
2082:  * 
2083:  * @param   array   $data   バリデーションを行う連想配列
2084:  * @return  boolean エラーが発生しなかった場合にはTRUE、そうでない場合にはFALSE
2085:  * @package validate
2086:  */
2087: function validate($data)
2088: {
2089:     global $__photon_form_rule;
2090:     $return = array();
2091:     foreach ($__photon_form_rule as $name => $rule) {
2092:         $value = array_get($data, $name);
2093:         __validate($data, $name, $rule, $value);
2094:     }
2095:     return form_has_error() ? FALSE : TRUE;
2096: }
2097: 
2098: /** @ignore */
2099: function __validate($data, $name, $rule, $value)
2100: {
2101:     // 入力必須
2102:     $required = isset($rule['required']) ? is_true($rule['required']) : FALSE;
2103:     if (is_array($value)) {
2104:         if (count($value) == 0) {
2105:             if ($required) {
2106:                 form_set_error($name, config('error_required'), $rule);
2107:             }
2108:         } else {
2109:             foreach ($value as $v) {
2110:                 $v = strval($v);
2111:                 if ($v !== '') {
2112:                     __validate($data, $name, $rule, $v);
2113:                 }
2114:             }
2115:         }
2116:         return;
2117:     } else {
2118:         $value = strval($value);
2119:         if ($value === '') {
2120:             if ($required) {
2121:                 form_set_error($name, config('error_required'), $rule);
2122:             }
2123:             return;
2124:         }
2125:     }
2126: 
2127:     // バイト数の範囲
2128:     $bytes = strlen($value);
2129:     if (isset($rule['min_bytes']) && $bytes < $rule['min_bytes']) {
2130:         return form_set_error($name, config('error_min_bytes'), $rule);
2131:     }
2132:     if (isset($rule['max_bytes']) && $bytes > $rule['max_bytes']) {
2133:         return form_set_error($name, config('error_max_bytes'), $rule);
2134:     }
2135: 
2136:     // 文字数の範囲
2137:     $chars = mb_strlen($value, 'utf-8');
2138:     if (isset($rule['min_chars']) && $chars < $rule['min_chars']) {
2139:         return form_set_error($name, config('error_min_chars'), $rule);
2140:     }
2141:     if (isset($rule['max_chars']) && $chars > $rule['max_chars']) {
2142:         return form_set_error($name, config('error_max_chars'), $rule);
2143:     }
2144: 
2145:     // 文字幅の範囲
2146:     $width = mb_strwidth($value, 'utf-8');
2147:     if (isset($rule['min_width']) && $width < $rule['min_width']) {
2148:         return form_set_error($name, config('error_min_width'), $rule);
2149:     }
2150:     if (isset($rule['max_width']) && $width > $rule['max_width']) {
2151:         return form_set_error($name, config('error_max_width'), $rule);
2152:     }
2153: 
2154:     // 行数の範囲
2155:     $lines = substr_count($value, "\n");
2156:     if (isset($rule['min_lines']) && $lines < $rule['min_lines']) {
2157:         return form_set_error($name, config('error_min_lines'), $rule);
2158:     }
2159:     if (isset($rule['max_lines']) && $lines > $rule['max_lines']) {
2160:         return form_set_error($name, config('error_max_lines'), $rule);
2161:     }
2162: 
2163:     // 値の範囲
2164:     if (isset($rule['min_value']) && $value < $rule['min_value']) {
2165:         return form_set_error($name, config('error_min_value'), $rule);
2166:     }
2167:     if (isset($rule['max_value']) && $value > $rule['max_value']) {
2168:         return form_set_error($name, config('error_max_value'), $rule);
2169:     }
2170: 
2171:     // 入力の一致を調べる
2172:     if (isset($rule['matches'])) {
2173:         if ($value !== strval(array_get($data, $rule['matches']))) {
2174:             return form_set_error($name, config('error_matches'), $rule);
2175:         }
2176:     }
2177: 
2178:     // 選択肢からの選択可を調べる
2179:     if (isset($rule['options'])) {
2180:         $options = form_get_options($rule['options']);
2181:         if (!isset($options[$value])) {
2182:             return form_set_error($name, config('error_options'), $rule);
2183:         }
2184:     }
2185: 
2186:     // Perl正規表現形式による書式
2187:     if (isset($rule['preg'])) {
2188:         if (!preg_match($rule['preg'], $value)) {
2189:             return form_set_error($name, config('error_preg'), $rule);
2190:         }
2191:     }
2192: 
2193:     // 入力の書式
2194:     $type = isset($rule['type']) ? $rule['type'] : '';
2195:     $value_int = intval($value);
2196:     switch ($type) {
2197:         case 'alpha':
2198:             if (preg_match('/[^a-zA-Z]/', $value)) {
2199:                 return form_set_error($name, config('error_alpha'), $rule);
2200:             }
2201:             break;
2202:         case 'digit':
2203:             if (preg_match('/[^0-9]/', $value)) {
2204:                 return form_set_error($name, config('error_digit'), $rule);
2205:             }
2206:             break;
2207:         case 'alnum':
2208:             if (preg_match('/[^a-zA-Z0-9]/', $value)) {
2209:                 return form_set_error($name, config('error_alnum'), $rule);
2210:             }
2211:             break;
2212:         case 'alnum_dash':
2213:             if (preg_match('/[^a-zA-Z0-9_-]/', $value)) {
2214:                 return form_set_error($name, config('error_alnum_dash'), $rule);
2215:             }
2216:             break;
2217:         case 'graph':
2218:             if (preg_match('/[^[:graph:]]/', $value)) {
2219:                 return form_set_error($name, config('error_graph'), $rule);
2220:             }
2221:             break;
2222:         case 'integer':
2223:             if (strval($value_int) !== $value) {
2224:                 return form_set_error($name, config('error_integer'), $rule);
2225:             }
2226:             break;
2227:         case 'natural':
2228:             if (strval($value_int) !== $value || $value_int < 0) {
2229:                 return form_set_error($name, config('error_natural'), $rule);
2230:             }
2231:             break;
2232:         case 'decimal':
2233:             if (!is_numeric($value)) {
2234:                 return form_set_error($name, config('error_decimal'), $rule);
2235:             }
2236:             break;
2237:         case 'mail':
2238:             if (!is_mail($value)) {
2239:                 return form_set_error($name, config('error_mail'), $rule);
2240:             }
2241:             break;
2242:         case 'ipv4':
2243:             if (!is_ipv4($value)) {
2244:                 return form_set_error($name, config('error_ipv4'), $rule);
2245:             }
2246:             break;
2247:         default:
2248:             break;
2249:     }
2250: }
2251: 
2252: //------------------------------------------------------------------------------
2253: // データベース
2254: //------------------------------------------------------------------------------
2255: 
2256: /**
2257:  * MySQLデータベースに接続する
2258:  * 
2259:  * config関数で設定した下記の設定オプションが使用される。
2260:  * 
2261:  * |* db_hostname   | データベースのホスト名
2262:  * |* db_username   | データベースのユーザ名
2263:  * |* db_password   | データベースのパスワード
2264:  * |* db_database   | データベースのデータベース
2265:  * |* db_charset    | データベースの文字コード
2266:  * 
2267:  * 失敗した場合にはfatal関数で強制終了する。
2268:  * 
2269:  * @return  resource    データベース接続ID
2270:  * @package db
2271:  */
2272: function db_connect()
2273: {
2274:     global $__photon_link;
2275: 
2276:     // 既に接続済みなら、接続IDを返す
2277:     if (isset($__photon_link) && $__photon_link !== FALSE) {
2278:         return $__photon_link;
2279:     }
2280: 
2281:     // MySQLデータベースに接続する
2282:     $db_hostname = config('db_hostname');
2283:     $db_username = config('db_username');
2284:     $db_password = config('db_password');
2285:     $__photon_link = @mysql_connect($db_hostname, $db_username, $db_password);
2286:     if ($__photon_link === FALSE) {
2287:         fatal(config('error_db_connect'));
2288:     }
2289: 
2290:     // MySQLデータベースを選択する
2291:     $db_database = config('db_database');
2292:     if (mysql_select_db($db_database, $__photon_link) === FALSE) {
2293:         fatal(config('error_db_select'));
2294:     }
2295: 
2296:     // 文字コードをutf-8に設定する
2297:     $db_charset = config('db_charset');
2298:     if (function_exists('mysql_set_charset')) {
2299:         if (mysql_set_charset($db_charset, $__photon_link) === FALSE) {
2300:             fatal(config('error_db_charset'));
2301:         }
2302:     } else {
2303:         if (mysql_query("SET NAMES " . $db_charset, $__photon_link) === FALSE) {
2304:             fatal(config('error_db_charset'));
2305:         }
2306:     }
2307: 
2308:     // 接続IDを返す
2309:     return $__photon_link;
2310: }
2311: 
2312: /**
2313:  * MySQLデータベースの接続を閉じる
2314:  * 
2315:  * @package db
2316:  */
2317: function db_close()
2318: {
2319:     global $__photon_link;
2320:     if (isset($__photon_link) && $__photon_link !== FALSE) {
2321:         mysql_close($__photon_link);
2322:         $__photon_link = FALSE;
2323:     }
2324: }
2325: 
2326: /**
2327:  * SQLクエリを実行する
2328:  * 
2329:  * 失敗した場合にはfatal関数で強制終了する
2330:  * 
2331:  * config関数で設定した下記の設定オプションが使用される。
2332:  * 
2333:  * |* log_query | クエリ履歴のテーブル名。空文字列で無効化
2334:  * 
2335:  * @param   string  $query  SQLクエリを表す文字列
2336:  * @return  mixed   結果のリソースID
2337:  * @package db
2338:  */
2339: function db_query($query)
2340: {
2341:     global $__photon_log_query;
2342: 
2343:     $link = db_connect();
2344: 
2345:     // クエリを実行する
2346:     $result = mysql_query($query, $link);
2347: 
2348:     // クエリに失敗した場合の処理
2349:     if ($result === FALSE) {
2350:         $message = config('error_db_query') . "\n";
2351:         $message .= mysql_error($link) . "\n" . $query;
2352:         fatal($message);
2353:     }
2354: 
2355:     // クエリ履歴に記録する
2356:     $log_query = config('log_query');
2357:     if ($log_query !== '') {
2358:         if (!isset($__photon_log_query)) {
2359:             $__photon_log_query = TRUE;
2360:             db_insert($log_query, compact('query'));
2361:             unset($__photon_log_query);
2362:         }
2363:     }
2364: 
2365:     return $result;
2366: }
2367: 
2368: /**
2369:  * 文字列を安全にSQLクエリ文に埋め込める形式にエスケープする
2370:  * 
2371:  * mysql_real_escape_string関数を呼び出すため、
2372:  * データベースへの接続を必要とする。
2373:  * 
2374:  * @param   string  $string 対象の文字列
2375:  * @return  string  エスケープされた文字列
2376:  * @package db
2377:  */
2378: function db_escape($string)
2379: {
2380:     return mysql_real_escape_string($string, db_connect());
2381: }
2382: 
2383: /**
2384:  * 文字列をクォートする
2385:  * 
2386:  * table.fieldの形式の場合、`table`.`field`の形式になる。
2387:  * fieldのみの場合、`field`の形式になる。
2388:  * いずれでもない場合、そのまま文字列を返す。
2389:  * 
2390:  * mysql_real_escape_string関数を呼び出すため、
2391:  * データベースへの接続を必要とする。
2392:  * 
2393:  * @param   string  $field  対象の文字列
2394:  * @return  string  エスケープされた文字列
2395:  * @package db
2396:  */
2397: function db_quote_field($field)
2398: {
2399:     if (preg_match('/^([a-z0-9_]+)(?:.([a-z0-9_*]+))?$/i', $field, $array)) {
2400:         $str = '`' . db_escape($array[1]) . '`';
2401:         if (isset($array[2])) {
2402:             if ($array[2] == '*') {
2403:                 $str .= '.*';
2404:             } else {
2405:                 $str .= '.`' . db_escape($array[2]) . '`';
2406:             }
2407:         }
2408:         return $str;
2409:     } else {
2410:         return $field;
2411:     }
2412: }
2413: 
2414: /**
2415:  * テーブルのカラムについての情報を取得する
2416:  * 
2417:  * DESCRIBE構文を実行する。結果はグローバル変数でキャッシュする。
2418:  * 
2419:  * @param   string  $table  テーブル名
2420:  * @return  array   テーブルのカラムについての情報
2421:  * @package db
2422:  */
2423: function db_describe($table)
2424: {
2425:     global $__photon_describe;
2426:     if (isset($__photon_describe[$table])) {
2427:         return $__photon_describe[$table];
2428:     } else {
2429:         $describe = db_select_table('describe `' . $table . '`', 'Field');
2430:         return $__photon_describe[$table] = $describe;
2431:     }
2432: }
2433: 
2434: /**
2435:  * テーブルのプライマリキーを取得する
2436:  * 
2437:  * @param   string  $table  テーブル名
2438:  * @return  string  プライマリキーのフィールド名
2439:  * @package db
2440:  */
2441: function db_primary_key($table)
2442: {
2443:     global $__photon_pk;
2444:     if (isset($__photon_pk[$table])) {
2445:         return $__photon_pk[$table];
2446:     } else {
2447:         $describe = db_describe($table);
2448:         foreach ($describe as $column) {
2449:             if ($column['Key'] == 'PRI') {
2450:                 return $__photon_pk[$table] = $column['Field'];
2451:             }
2452:         }
2453:         return NULL;
2454:     }
2455: }
2456: 
2457: /**
2458:  * テーブルのデータを挿入可能な形式に変換する
2459:  * 
2460:  * $dataの要素名が$tableのフィールド名と一致するものだけを抽出する。
2461:  * 
2462:  * @param   string  $table  テーブル名
2463:  * @param   array   $data   対象の連想配列
2464:  * @return  string  プライマリキーのフィールド名
2465:  * @package db
2466:  */
2467: function db_convert($table, $data)
2468: {
2469:     $keys = array_keys(db_describe($table));
2470:     $return = array();
2471:     foreach ($keys as $key) {
2472:         if (isset($data[$key])) {
2473:             $return[$key] = $data[$key];
2474:         }
2475:     }
2476:     return $return;
2477: }
2478: 
2479: /**
2480:  * トランザクションを開始する
2481:  * 
2482:  * @package db
2483:  */
2484: function db_start_transaction()
2485: {
2486:     global $__photon_transaction;
2487:     $__photon_transaction = true;
2488:     db_query('START TRANSACTION');
2489: }
2490: 
2491: /**
2492:  * コミット処理を行う
2493:  * 
2494:  * @package db
2495:  */
2496: function db_commit()
2497: {
2498:     global $__photon_transaction;
2499:     if ($__photon_transaction) {
2500:         $__photon_transaction = false;
2501:         db_query('COMMIT');
2502:     }
2503: }
2504: 
2505: /**
2506:  * ロールバック処理を行う
2507:  * 
2508:  * @package db
2509:  */
2510: function db_rollback()
2511: {
2512:     global $__photon_link;
2513:     global $__photon_transaction;
2514:     if ($__photon_link && $__photon_transaction) {
2515:         $__photon_transaction = false;
2516:         db_query('ROLLBACK');
2517:     }
2518: }
2519: 
2520: /**
2521:  * クエリ文を実行し、結果をレコードの配列で取得する
2522:  * 
2523:  * @param   string  $query      クエリ文
2524:  * @param   string  $key_field  レコードのキーとするフィールド名
2525:  * @return  array   結果のレコードの配列
2526:  * @package db
2527:  */
2528: function db_select_table($query, $key_field = NULL)
2529: {
2530:     $result = db_query($query);
2531:     $return = array();
2532:     while ($row = mysql_fetch_assoc($result)) {
2533:         if ($key_field === NULL) {
2534:             $return[] = $row;
2535:         } else {
2536:             $return[$row[$key_field]] = $row;
2537:         }
2538:     }
2539:     mysql_free_result($result);
2540:     return $return;
2541: }
2542: 
2543: /**
2544:  * クエリ文を実行し、結果をレコードで取得する
2545:  * 
2546:  * @param   string  $query      クエリ文
2547:  * @param   string  $row_number 取得する行番号
2548:  * @return  array   結果のレコード
2549:  * @package db
2550:  */
2551: function db_select_row($query, $row_number = 0)
2552: {
2553:     $result = db_query($query);
2554:     if ($row_number != 0) {
2555:         mysql_data_seek($result, $row_number);
2556:     }
2557:     $return = mysql_fetch_assoc($result);
2558:     mysql_free_result($result);
2559:     return $return;
2560: }
2561: 
2562: /**
2563:  * クエリ文を実行し、結果を列の連想配列で取得する
2564:  * 
2565:  * @param   string  $query          クエリ文
2566:  * @param   string  $value_field    値として取得するフィールド名
2567:  * @param   string  $key_field      キーとして取得するフィールド名
2568:  * @return  array   結果の連想配列
2569:  * @package db
2570:  */
2571: function db_select_column($query, $value_field = NULL, $key_field = NULL)
2572: {
2573:     $result = db_query($query);
2574:     $return = array();
2575:     while ($row = mysql_fetch_assoc($result)) {
2576:         if ($value_field === NULL) {
2577:             $value = current($row);
2578:         } else {
2579:             $value = $row[$value_field];
2580:         }
2581:         if ($key_field === NULL) {
2582:             $return[] = $value;
2583:         } else {
2584:             $return[$row[$key_field]] = $value;
2585:         }
2586:     }
2587:     mysql_free_result($result);
2588:     return $return;
2589: }
2590: 
2591: /**
2592:  * クエリ文を実行し、結果の値を取得する
2593:  * 
2594:  * @param   string  $query          クエリ文
2595:  * @param   string  $value_field    値として取得するフィールド名
2596:  * @param   string  $row_number     取得する行番号
2597:  * @return  string  取得結果の値
2598:  * @package db
2599:  */
2600: function db_select_value($query, $value_field = NULL, $row_number = 0)
2601: {
2602:     $result = db_query($query);
2603:     if ($row_number != 0) {
2604:         mysql_data_seek($result, $row_number);
2605:     }
2606:     $row = mysql_fetch_assoc($result);
2607:     if ($value_field === NULL) {
2608:         $return = current($row);
2609:     } else {
2610:         $return = $row[$value_field];
2611:     }
2612:     mysql_free_result($result);
2613:     return $return;
2614: }
2615: 
2616: /**
2617:  * 条件をフィールド名と値で指定し、レコードを取得する
2618:  * 
2619:  * $cond_fieldが省略された場合、テーブルのプライマリキーを使用する。
2620:  * 
2621:  * @param   string  $table      テーブル名
2622:  * @param   string  $cond_value 条件の値
2623:  * @param   string  $cond_field 条件のフィールド名
2624:  * @return  array   結果のレコード
2625:  * @package db
2626:  */
2627: function db_select_at($table, $cond_value, $cond_field = NULL)
2628: {
2629:     if ($cond_field === NULL) {
2630:         $cond_field = db_primary_key($table);
2631:         if ($cond_field === NULL) {
2632:             fatal(config('error_primary_key'));
2633:         }
2634:     }
2635:     $query = 'SELECT * FROM `' . db_escape($table);
2636:     $query .= '` WHERE `' . db_escape($cond_field) . "`='";
2637:     $query .= db_escape($cond_value) . "' LIMIT 1";
2638:     return db_select_row($query);
2639: }
2640: 
2641: /**
2642:  * 条件を連想配列で指定し、レコードの配列を取得する
2643:  * 
2644:  * @param   string  $table      テーブル名
2645:  * @param   string  $cond       条件の連想配列
2646:  * @return  array   結果のレコード
2647:  * @package db
2648:  */
2649: function db_select($table, $cond)
2650: {
2651:     $query = 'SELECT * FROM `' . db_escape($table);
2652:     $query .= '` WHERE ' . __db_where_assoc($cond);
2653:     return db_select_table($query);
2654: }
2655: 
2656: /**
2657:  * テーブルから選択肢を生成する
2658:  * 
2659:  * フォームの選択肢として有用な選択肢の連想配列を生成する。
2660:  * 
2661:  * $nameはデフォルトでname、title、プライマリキーを使用する。
2662:  * $valueはデフォルトでプライマリキーを使用する。
2663:  * 
2664:  * 選択肢の連想配列は、orderという名前のフィールドが存在する場合には、
2665:  * orderの数値の降順となるが、存在しない場合には$valueの順序となる。
2666:  * 
2667:  * @param   string  $table  テーブル名
2668:  * @param   string  $name   選択肢のラベルのフィールド名
2669:  * @param   string  $value  選択肢の値のフィールド名
2670:  * @param   array   $cond   選択肢の条件
2671:  * @return  array   選択肢の連想配列
2672:  * @package db
2673:  */
2674: function db_select_options($table, $name = NULL, $value = NULL, $cond = NULL)
2675: {
2676:     $describe = db_describe($table);
2677: 
2678:     // $nameが省略された場合
2679:     if ($name === NULL) {
2680:         if (isset($describe['name'])) {
2681:             $name = 'name';
2682:         } elseif (isset($describe['title'])) {
2683:             $name = 'title';
2684:         } else {
2685:             $name = db_primary_key($table);
2686:         }
2687:     }
2688: 
2689:     // $valueが省略された場合
2690:     if ($value === NULL) {
2691:         $value = db_primary_key($table);
2692:     }
2693: 
2694:     // クエリ文を構築
2695:     $query = 'SELECT `' . db_escape($name) . '`,`';
2696:     $query .= db_escape($value) . '`FROM `' . db_escape($table);
2697:     $query .= '` WHERE ' . __db_where_assoc(db_convert($table, $cond));
2698:     if (isset($describe['order'])) {
2699:         $query .= ' ORDER BY `order` DESC';
2700:     } else {
2701:         $query .= ' ORDER BY `' . db_escape($value) . '`';
2702:     }
2703: 
2704:     // クエリを実行
2705:     return db_select_column($query, $name, $value);
2706: }
2707: 
2708: /**
2709:  * テーブルにレコードを挿入する
2710:  * 
2711:  * createdとupdatedが現在の日時に設定される。
2712:  * $idでプライマリキーの値を指定できる。
2713:  * 
2714:  * @param   string  $table      テーブル名
2715:  * @param   string  $data       挿入するレコードの連想配列
2716:  * @param   string  $id         プライマリキーの値
2717:  * @return  integer プライマリキーの値
2718:  * @package db
2719:  */
2720: function db_insert($table, $data, $id = NULL)
2721: {
2722:     // データの準備
2723:     $pk = db_primary_key($table);
2724:     $data['created'] = $data['updated'] = __db_timestamp();
2725:     $data = db_convert($table, $data);
2726:     if ($pk !== NULL) {
2727:         if ($id === NULL) {
2728:             unset($data[$pk]);
2729:         } else {
2730:             $data[$pk] = $id;
2731:         }
2732:     }
2733: 
2734:     // INSERT文を生成
2735:     $query_field = $query_value = '';
2736:     foreach ($data as $field => $value) {
2737:         $query_field .= '`' . db_escape($field) . '`,';
2738:         $query_value .= "'" . db_escape($value) . "',";
2739:     }
2740:     $query_field = rtrim($query_field, ',');
2741:     $query_value = rtrim($query_value, ',');
2742:     $query = 'INSERT INTO `' . db_escape($table) . '` (';
2743:     $query .= $query_field . ') VALUES (' . $query_value . ')';
2744: 
2745:     // クエリを実行
2746:     db_query($query);
2747: 
2748:     // 生成したIDを返す
2749:     return mysql_insert_id(db_connect());
2750: }
2751: 
2752: /**
2753:  * テーブルを更新する
2754:  * 
2755:  * updatedが現在の日時に設定される。
2756:  * 
2757:  * @param   string  $table  テーブル名
2758:  * @param   string  $cond   更新する条件の連想配列
2759:  * @param   string  $data   更新する値の連想配列
2760:  * @return  integer 更新されたレコード数
2761:  * @package db
2762:  */
2763: function db_update($table, $data, $cond)
2764: {
2765:     // データの準備
2766:     $pk = db_primary_key($table);
2767:     $data['updated'] = __db_timestamp();
2768:     $data = db_convert($table, $data);
2769:     $cond = db_convert($table, $cond);
2770:     if (count($cond) == 0) {
2771:         fatal(config('error_invalid_condition'));
2772:     }
2773:     if ($pk !== NULL) {
2774:         unset($data[$pk]);
2775:     }
2776: 
2777:     // UPDATE文を生成
2778:     $query = 'UPDATE `' . db_escape($table) . '` SET ';
2779:     foreach ($data as $field => $value) {
2780:         $query .= '`' . db_escape($field) . "`='" . db_escape($value) . "',";
2781:     }
2782:     $query = rtrim($query, ',') . ' WHERE ' . __db_where_assoc($cond);
2783: 
2784:     // クエリを実行
2785:     db_query($query);
2786: 
2787:     // 影響したIDを返す
2788:     return mysql_affected_rows(db_connect());
2789: }
2790: 
2791: /**
2792:  * テーブルを更新する
2793:  * 
2794:  * updatedが現在の日時に設定される。
2795:  * 
2796:  * @param   string  $table      テーブル名
2797:  * @param   string  $data       更新する値の連想配列
2798:  * @param   string  $cond_value 条件の値
2799:  * @param   string  $cond_field 条件のフィールド名
2800:  * @return  integer 更新されたレコード数
2801:  * @package db
2802:  */
2803: function db_update_at($table, $data, $cond_value, $cond_field = NULL)
2804: {
2805:     if ($cond_field === NULL) {
2806:         $cond_field = db_primary_key($table);
2807:         if ($cond_field === NULL) {
2808:             fatal(config('error_primary_key'));
2809:         }
2810:     }
2811:     return db_update($table, $data, array($cond_field => $cond_value));
2812: }
2813: 
2814: /**
2815:  * テーブルを削除する
2816:  * 
2817:  * @param   string  $table  テーブル名
2818:  * @param   string  $cond   削除する条件の連想配列
2819:  * @return  integer 削除されたレコード数
2820:  * @package db
2821:  */
2822: function db_delete($table, $cond)
2823: {
2824:     // データの準備
2825:     $cond = db_convert($table, $cond);
2826:     if (count($cond) == 0) {
2827:         fatal(config('error_invalid_condition'));
2828:     }
2829: 
2830:     // DELETE文を生成
2831:     $query = 'DELETE FROM `' . db_escape($table) . '` WHERE ';
2832:     $query .= __db_where_assoc($cond);
2833: 
2834:     // クエリを実行
2835:     db_query($query);
2836: 
2837:     // 影響したIDを返す
2838:     return mysql_affected_rows(db_connect());
2839: }
2840: 
2841: /**
2842:  * テーブルを削除する
2843:  * 
2844:  * @param   string  $table      テーブル名
2845:  * @param   string  $cond_value 条件の値
2846:  * @param   string  $cond_field 条件のフィールド名
2847:  * @return  integer 削除されたレコード数
2848:  * @package db
2849:  */
2850: function db_delete_at($table, $cond_value, $cond_field = NULL)
2851: {
2852:     if ($cond_field === NULL) {
2853:         $cond_field = db_primary_key($table);
2854:         if ($cond_field === NULL) {
2855:             fatal(config('error_primary_key'));
2856:         }
2857:     }
2858:     return db_delete($table, array($cond_field => $cond_value));
2859: }
2860: 
2861: /**
2862:  * テーブルを挿入・更新する
2863:  * 
2864:  * $dataにプライマリキーが指定されている場合には更新処理を行う。
2865:  * そうでない場合には挿入処理を行う。
2866:  * 
2867:  * updatedが現在の日時に設定される。
2868:  * 
2869:  * @param   string  $table      テーブル名
2870:  * @param   string  $data       更新する値の連想配列
2871:  * @return  integer プライマリキーの値
2872:  * @package db
2873:  */
2874: function db_replace($table, $data)
2875: {
2876:     $pk = db_primary_key($table);
2877:     if (isset($data[$pk])) {
2878:         $id = $data[$pk];
2879:         db_update_at($table, $data, $id);
2880:         return $id;
2881:     } else {
2882:         return db_insert($table, $data);
2883:     }
2884: }
2885: 
2886: /**
2887:  * MySQLの日時を生成する
2888:  * 
2889:  * @param   string  $timestamp  UNIXタイムスタンプ
2890:  * @return  string  MySQLの日時文字列
2891:  * @package db
2892:  */
2893: function db_datetime($timestamp = NULL)
2894: {
2895:     if ($timestamp === NULL) {
2896:         $timestamp = time();
2897:     }
2898:     return date('Y-m-d H:i:s', $timestamp);
2899: }
2900: 
2901: /** @ignore */
2902: function __db_where_assoc($cond)
2903: {
2904:     $query = '1=1';
2905:     foreach ($cond as $field => $value) {
2906:         $query .= ' AND `' . db_escape($field);
2907:         $query .= "`='" . db_escape($value) . "'";
2908:     }
2909:     return $query;
2910: }
2911: 
2912: /** @ignore */
2913: function __db_timestamp()
2914: {
2915:     global $__photon_timestamp;
2916:     if (isset($__photon_timestamp)) {
2917:         return $__photon_timestamp;
2918:     } else {
2919:         return $__photon_timestamp = db_datetime();
2920:     }
2921: }
2922: 
2923: //------------------------------------------------------------------------------
2924: // ページネーション
2925: //------------------------------------------------------------------------------
2926: 
2927: /**
2928:  * ページネーションのHTMLを生成する
2929:  * 
2930:  * ページネーションの入力値として、下記の連想配列の要素を設定する。
2931:  * 
2932:  * |* per_page  | ページ毎のデータ数
2933:  * |* num_links | 表示するページ番号の数
2934:  * |* first_tag | 最初に移動するタグ
2935:  * |* prev_tag  | 前に移動するタグ
2936:  * |* link_tag  | ページ番号に移動するタグ
2937:  * |* active_tag| 現在のページのタグ
2938:  * |* next_tag  | 次に移動するタグ
2939:  * |* last_tag  | 最後に移動するタグ
2940:  * |* open_tag  | ページネーションの開始タグ
2941:  * |* close_tag | ページネーションの終了タグ
2942:  * |* url       | リンク先のURL
2943:  * |* page      | 現在のページ番号
2944:  * 
2945:  * config関数で設定した下記の設定オプションが
2946:  * デフォルト値として使用される。
2947:  * 
2948:  * - paginate_per_page
2949:  * - paginate_num_links
2950:  * - paginate_first_tag
2951:  * - paginate_prev_tag
2952:  * - paginate_link_tag
2953:  * - paginate_active_tag
2954:  * - paginate_next_tag
2955:  * - paginate_last_tag
2956:  * - paginate_open_tag
2957:  * - paginate_close_tag
2958:  * 
2959:  * ページネーションの返り値として、下記の連想配列の要素が設定される
2960:  * 
2961:  * |* offset    | 表示開始する行番号
2962:  * |* limit     | 表示する行数
2963:  * |* html      | HTMLタグ
2964:  * 
2965:  * @param   integer $total_rows データ数
2966:  * @param   array   $p          ページネーションの入力値
2967:  * @return  array   ページネーションの出力値
2968:  * @package paginate
2969:  */
2970: function paginate($total_rows, $p = NULL)
2971: {
2972:     // ページネーションを設定
2973:     if ($p === NULL) {
2974:         $p = array();
2975:     }
2976:     if (!isset($p['per_page'])) {
2977:         $p['per_page'] = config('paginate_per_page');
2978:     }
2979:     if (!isset($p['num_links'])) {
2980:         $p['num_links'] = config('paginate_num_links');
2981:     }
2982:     if (!isset($p['first_tag'])) {
2983:         $p['first_tag'] = config('paginate_first_tag');
2984:     }
2985:     if (!isset($p['prev_tag'])) {
2986:         $p['prev_tag'] = config('paginate_prev_tag');
2987:     }
2988:     if (!isset($p['link_tag'])) {
2989:         $p['link_tag'] = config('paginate_link_tag');
2990:     }
2991:     if (!isset($p['active_tag'])) {
2992:         $p['active_tag'] = config('paginate_active_tag');
2993:     }
2994:     if (!isset($p['next_tag'])) {
2995:         $p['next_tag'] = config('paginate_next_tag');
2996:     }
2997:     if (!isset($p['last_tag'])) {
2998:         $p['last_tag'] = config('paginate_last_tag');
2999:     }
3000:     if (!isset($p['open_tag'])) {
3001:         $p['open_tag'] = config('paginate_open_tag');
3002:     }
3003:     if (!isset($p['close_tag'])) {
3004:         $p['close_tag'] = config('paginate_close_tag');
3005:     }
3006:     if (!isset($p['url'])) {
3007:         $p['url'] = get_request_url();
3008:     }
3009:     if (!isset($p['page'])) {
3010:         if (isset($_REQUEST['page'])) {
3011:             $p['page'] = $_REQUEST['page'];
3012:         } else {
3013:             $p['page'] = 1;
3014:         }
3015:     }
3016: 
3017:     // ページ数
3018:     $total_pages = max(ceil($total_rows / $p['per_page']), 1);
3019: 
3020:     // ページ番号を範囲内に収める
3021:     $p['page'] = limit_range($p['page'], 1, $total_pages);
3022: 
3023:     // 行の範囲を求める
3024:     $p['offset'] = ($p['page'] - 1) * $p['per_page'];
3025:     $p['limit'] = min($total_rows - $p['offset'], $p['per_page']);
3026: 
3027:     // ページ番号の範囲を求める
3028:     $page_start = max($p['page'] - floor($p['num_links'] / 2), 1);
3029:     $page_end = min($page_start + $p['num_links'] - 1, $total_pages);
3030:     $page_start = min($page_start, max(1, $page_end - $p['num_links'] + 1));
3031: 
3032:     // HTMLを生成する
3033:     $html = '';
3034:     if ($p['page'] != 1) {
3035:         $html .= __paginate_tag(1, $p['url'], $p['first_tag']);
3036:         $html .= __paginate_tag($p['page'] - 1, $p['url'], $p['prev_tag']);
3037:     }
3038:     for ($page = $page_start; $page <= $page_end; $page++) {
3039:         if ($p['page'] == $page) {
3040:             $html .= __paginate_tag($page, $p['url'], $p['active_tag']);
3041:         } else {
3042:             $html .= __paginate_tag($page, $p['url'], $p['link_tag']);
3043:         }
3044:     }
3045:     if ($p['page'] != $page_end) {
3046:         $html .= __paginate_tag($p['page'] + 1, $p['url'], $p['next_tag']);
3047:         $html .= __paginate_tag($total_pages, $p['url'], $p['last_tag']);
3048:     }
3049:     $p['html'] = $p['open_tag'] . $html . $p['close_tag'];
3050: 
3051:     return $p;
3052: }
3053: 
3054: /** @ignore */
3055: function __paginate_tag($page, $url, $tag)
3056: {
3057:     $vars = array();
3058:     $vars['page'] = $page;
3059:     $vars['url'] = modify_url_query($url, array('page' => $page));
3060:     return embed($tag, $vars);
3061: }
3062: 
3063: /**
3064:  * クエリを実行し、ページネーションのHTMLを生成する
3065:  * 
3066:  * ページネーションの入力値として、下記の連想配列の要素を設定する
3067:  * 
3068:  * |* query_count   | データ数を求めるクエリ文
3069:  * 
3070:  * ページネーションの返り値として、下記の連想配列の要素が設定される
3071:  * 
3072:  * |* records       | 取得したレコードの配列
3073:  * 
3074:  * @param   string  $query  クエリ文
3075:  * @param   array   $p      ページネーションの入力値
3076:  * @return  array   ページネーションの出力値
3077:  * @package paginate
3078:  */
3079: function db_paginate($query, $p = NULL)
3080: {
3081:     // データ数を求める
3082:     if (isset($p['query_count'])) {
3083:         $query_count = $p['query_count'];
3084:     } else {
3085:         $query_count = 'SELECT COUNT(*) FROM (' . $query . ') AS __db_paginate';
3086:     }
3087:     $total_rows = db_select_value($query_count);
3088: 
3089:     // ページネーションのHTMLを生成する
3090:     $p = paginate($total_rows, $p);
3091: 
3092:     // レコードを取得する
3093:     $query .= ' LIMIT ' . $p['offset'] . ',' . $p['limit'];
3094:     $p['records'] = db_select_table($query);
3095: 
3096:     return $p;
3097: }
3098: 
3099: //------------------------------------------------------------------------------
3100: // SQLクエリ
3101: //------------------------------------------------------------------------------
3102: 
3103: /**
3104:  * SQLクエリ文の生成を開始する
3105:  * 
3106:  * 現在の設定をスタックに保存する。
3107:  * SQLクエリ文の生成条件はスタック可能であり、sql_beginは何度も呼び出せる。
3108:  * 
3109:  * @package sql
3110:  */
3111: function sql_push()
3112: {
3113:     global $__photon_sql;
3114:     global $__photon_sql_stack;
3115: 
3116:     if (!isset($__photon_sql_stack)) {
3117:         $__photon_sql_stack = array();
3118:     }
3119:     array_push($__photon_sql_stack, $__photon_sql);
3120:     sql_clean();
3121: }
3122: 
3123: /**
3124:  * SQLクエリ文の生成を終了する
3125:  * 
3126:  * 最後にsql_beginを呼んだ際のSQLクエリ文の生成条件に復帰する。
3127:  * 
3128:  * @package sql
3129:  */
3130: function sql_pop()
3131: {
3132:     global $__photon_sql;
3133:     global $__photon_sql_stack;
3134: 
3135:     $__photon_sql = array_pop($__photon_sql_stack);
3136: }
3137: 
3138: /**
3139:  * 処理対象のテーブル名を設定する
3140:  * 
3141:  * @param   string  $table  テーブル名
3142:  * @param   string  $alias  エイリアス名
3143:  * @package sql
3144:  */
3145: function sql_table($table, $alias = NULL)
3146: {
3147:     global $__photon_sql;
3148: 
3149:     $__photon_sql['table'] = $table;
3150:     $__photon_sql['alias'] = $alias;
3151: }
3152: 
3153: /**
3154:  * SQLクエリ文の生成条件をクリアする
3155:  * 
3156:  * @package sql
3157:  */
3158: function sql_clean()
3159: {
3160:     global $__photon_sql;
3161: 
3162:     $__photon_sql = array();
3163: }
3164: 
3165: /**
3166:  * SELECT文を生成する
3167:  * 
3168:  * 下記のSQL文の生成条件が有効となる。
3169:  * - sql_field
3170:  * - sql_join
3171:  * - sql_where
3172:  * - sql_group
3173:  * - sql_order
3174:  * - sql_limit
3175:  * 
3176:  * @param   string  $table  テーブル名
3177:  * @param   string  $alias  エイリアス名
3178:  * @return  string  生成されたSQL文字列
3179:  * @package sql
3180:  */
3181: function sql_select($table = NULL, $alias = NULL)
3182: {
3183:     global $__photon_sql;
3184: 
3185:     if ($table === NULL) {
3186:         $table = $__photon_sql['table'];
3187:     }
3188:     if ($alias === NULL) {
3189:         $alias = $__photon_sql['alias'];
3190:     }
3191:     $query = 'SELECT ';
3192:     if (isset($__photon_sql['field'])) {
3193:         $query .= rtrim($__photon_sql['field'], ',');
3194:     } else {
3195:         $query .= '`' . db_escape($table) . '`.*';
3196:     }
3197:     $query .= ' FROM `' . db_escape($table) . '`';
3198:     if ($alias !== NULL) {
3199:         $query .= ' AS `' . db_escape($alias) . '`';
3200:     }
3201:     if (isset($__photon_sql['join'])) {
3202:         $query .= ' ' . $__photon_sql['join'];
3203:     }
3204:     if (isset($__photon_sql['where'])) {
3205:         $query .= ' WHERE ' . $__photon_sql['where'];
3206:     }
3207:     if (isset($__photon_sql['group'])) {
3208:         $query .= ' GROUP BY ' . $__photon_sql['group'];
3209:     }
3210:     if (isset($__photon_sql['order'])) {
3211:         $query .= ' ORDER BY ' . rtrim($__photon_sql['order'], ',');
3212:     }
3213:     if (isset($__photon_sql['limit'])) {
3214:         $query .= ' LIMIT ' . $__photon_sql['limit'];
3215:     }
3216:     return $query;
3217: }
3218: 
3219: /**
3220:  * INSERT文を生成する
3221:  * 
3222:  * 下記のSQL文の生成条件が有効となる。
3223:  * - sql_value
3224:  * 
3225:  * @param   string  $table  テーブル名
3226:  * @param   string  $ignore キーが重複する際に無視する場合はTRUE
3227:  * @return  string  生成されたSQL文字列
3228:  * @package sql
3229:  */
3230: function sql_insert($table = NULL, $ignore = FALSE)
3231: {
3232:     global $__photon_sql;
3233: 
3234:     if ($table === NULL) {
3235:         $table = $__photon_sql['table'];
3236:     }
3237:     if ($ignore) {
3238:         $query = 'INSERT IGNORE INTO ';
3239:     } else {
3240:         $query = 'INSERT INTO ';
3241:     }
3242:     $query .= '`' . db_escape($table) . '` (';
3243:     if (isset($__photon_sql['insert_field'])) {
3244:         $query .= rtrim($__photon_sql['insert_field'], ',');
3245:     }
3246:     $query .= ') VALUES (';
3247:     if (isset($__photon_sql['insert_value'])) {
3248:         $query .= rtrim($__photon_sql['insert_value'], ',');
3249:     }
3250:     $query .= ')';
3251:     return $query;
3252: }
3253: 
3254: /**
3255:  * UDPATE文を生成する
3256:  * 
3257:  * 下記のSQL文の生成条件が有効となる。
3258:  * - sql_set
3259:  * - sql_where
3260:  * 
3261:  * @param   string  $table  テーブル名
3262:  * @return  string  生成されたSQL文字列
3263:  * @package sql
3264:  */
3265: function sql_update($table = NULL)
3266: {
3267:     global $__photon_sql;
3268: 
3269:     if ($table === NULL) {
3270:         $table = $__photon_sql['table'];
3271:     }
3272:     $query = 'UPDATE `' . db_escape($table) . '` SET ';
3273:     if (isset($__photon_sql['set'])) {
3274:         $query .= rtrim($__photon_sql['set'], ',');
3275:     }
3276:     if (isset($__photon_sql['where'])) {
3277:         $query .= ' WHERE ' . $__photon_sql['where'];
3278:     }
3279:     return $query;
3280: }
3281: 
3282: /**
3283:  * DELETE文を生成する
3284:  * 
3285:  * 下記のSQL文の生成条件が有効となる。
3286:  * - sql_where
3287:  * 
3288:  * @param   string  $table  テーブル名
3289:  * @return  string  生成されたSQL文字列
3290:  * @package sql
3291:  */
3292: function sql_delete($table = NULL)
3293: {
3294:     global $__photon_sql;
3295: 
3296:     $query = 'DELETE FROM `' . db_escape($table) . '`';
3297:     if (isset($__photon_sql['where'])) {
3298:         $query .= ' WHERE ' . $__photon_sql['where'];
3299:     }
3300:     return $query;
3301: }
3302: 
3303: /**
3304:  * SELECT文で取得するフィールドを追加する
3305:  * 
3306:  * $fieldにはdb_quote_fieldでの指定形式が使用できる。
3307:  * フィールド名単体、テーブル名とフィールド名の指定、式の指定ができる。
3308:  * 
3309:  * @param   string  $field  取得するフィールド
3310:  * @param   string  $alias  エイリアス名
3311:  * @package sql
3312:  */
3313: function sql_field($field, $alias = NULL)
3314: {
3315:     $str = db_quote_field($field);
3316:     if ($alias !== NULL) {
3317:         $str .= ' AS `' . db_escape($alias) . '`';
3318:     }
3319:     $str .= ',';
3320:     __sql_append('field', $str);
3321: }
3322: 
3323: /**
3324:  * 他テーブルとの結合条件を追加する
3325:  * 
3326:  * 下記の形式で結合条件が追加される。
3327:  * LEFT JOIN `$join_fieldのテーブル名` ON $join_field=$cond_field
3328:  * 
3329:  * $cond_fieldにはdb_quote_fieldでの指定形式が使用できる。
3330:  * フィールド名単体、テーブル名とフィールド名の指定、式の指定ができる。
3331:  * 
3332:  * @param   string  $table      結合先のテーブル名
3333:  * @param   string  $field      結合先のフィールド名
3334:  * @param   string  $cond_table 条件のテーブル名
3335:  * @param   string  $cond_field 条件のフィールド名
3336:  * @param   string  $alias      結合するテーブルのエイリアス名
3337:  * @package sql
3338:  */
3339: function sql_join($table, $field, $cond_table, $cond_field, $alias = NULL)
3340: {
3341:     $str = 'LEFT JOIN `' . db_escape($table) . '`';
3342:     if ($alias !== NULL) {
3343:         $str .= ' AS `' . db_escape($alias) . '`';
3344:         $table = $alias;
3345:     }
3346:     $str .= ' ON `' . db_escape($table) . '`.`' . db_escape($field) . '`=`';
3347:     $str .= db_escape($cond_table) . '`.`' . db_escape($cond_field) . '`';
3348:     __sql_append('join', $str);
3349: }
3350: 
3351: /**
3352:  * WHERE節の指定を開始する
3353:  * 
3354:  * 通常、sql_where系で条件を指定する場合AND条件となるが、一部をOR条件等で
3355:  * 結合する場合、sql_where_beginで論理演算子を指定し、sql_where_endで終了する。
3356:  * 
3357:  * WHERE条件はスタック可能であり、sql_where_beginは何度も呼び出せる。
3358:  * 
3359:  * @param   string  $glue   条件を結合する論理演算子
3360:  * @package sql
3361:  */
3362: function sql_where_begin($glue)
3363: {
3364:     global $__photon_sql;
3365: 
3366:     // 論理演算子をスタックに保存
3367:     if (!isset($__photon_sql['where_stack'])) {
3368:         $__photon_sql['where_stack'] = array();
3369:     }
3370:     if (isset($__photon_sql['where_glue'])) {
3371:         array_push($__photon_sql['where_stack'], $__photon_sql['where_glue']);
3372:     } else {
3373:         array_push($__photon_sql['where_stack'], 'AND');
3374:     }
3375: 
3376:     // WHERE節に追加
3377:     $glue = strtoupper($glue);
3378:     if ($glue === 'AND') {
3379:         sql_where('(1=1');
3380:     } else {
3381:         sql_where('(1=0');
3382:     }
3383: 
3384:     // 論理演算子を設定
3385:     $__photon_sql['where_glue'] = ' ' . $glue . ' ';
3386: }
3387: 
3388: /**
3389:  * WHERE節の指定を開始する
3390:  * 
3391:  * 前にsql_where_beginを呼び出した際の論理演算子に復帰する。
3392:  * @package sql
3393:  */
3394: function sql_where_end()
3395: {
3396:     global $__photon_sql;
3397: 
3398:     // 論理演算子を復帰
3399:     $__photon_sql['where_glue'] = array_pop($__photon_sql['where_stack']);
3400: 
3401:     // WHERE節に追加
3402:     __sql_append('where', ')');
3403: }
3404: 
3405: /**
3406:  * WHERE節に条件文を加える
3407:  * 
3408:  * @param   string  $expr   条件を表す文字列
3409:  * @package sql
3410:  */
3411: function sql_where($expr)
3412: {
3413:     global $__photon_sql;
3414: 
3415:     if (!isset($__photon_sql['where_glue'])) {
3416:         __sql_append('where', '1=1 AND ' . $expr);
3417:         $__photon_sql['where_glue'] = ' AND ';
3418:     } else {
3419:         __sql_append('where', $__photon_sql['where_glue'] . $expr);
3420:     }
3421: }
3422: 
3423: /**
3424:  * 文字列との比較をWHERE節に追加する
3425:  * 
3426:  * @param   string  $field      フィールドの条件 (db_quote_fieldの形式)
3427:  * @param   string  $value      比較する値
3428:  * @param   string  $operator   比較演算子
3429:  * @package sql
3430:  */
3431: function sql_where_string($field, $value, $operator = '=')
3432: {
3433:     $str = db_quote_field($field) . $operator . "'" . db_escape($value) . "'";
3434:     sql_where($str);
3435: }
3436: 
3437: /**
3438:  * 整数値との比較をWHERE節に追加する
3439:  * 
3440:  * @param   string  $field      フィールドの条件 (db_quote_fieldの形式)
3441:  * @param   integer $value      比較する値
3442:  * @param   string  $operator   比較演算子
3443:  * @package sql
3444:  */
3445: function sql_where_integer($field, $value, $operator = '=')
3446: {
3447:     $str = db_quote_field($field) . $operator . intval($value);
3448:     sql_where($str);
3449: }
3450: 
3451: /**
3452:  * 範囲条件をWHERE節に追加する
3453:  * 
3454:  * @param   string  $field  フィールドの条件 (db_quote_fieldの形式)
3455:  * @param   string  $min    下限値
3456:  * @param   string  $max    上限値
3457:  * @package sql
3458:  */
3459: function sql_where_between($field, $min, $max)
3460: {
3461:     $str = db_quote_field($field) . " BETWEEN '";
3462:     $str .= db_escape($min) . "' AND '" . db_escape($max) . "'";
3463:     sql_where($str);
3464: }
3465: 
3466: /**
3467:  * キーワード検索をWHERE節に追加する
3468:  * 
3469:  * @param   string  $fields     対象となるフィールド、またはフィールドの配列
3470:  * @param   string  $keywords   空白で区切られた検索キーワード
3471:  * @package sql
3472:  */
3473: function sql_where_search($fields, $keywords)
3474: {
3475:     if (!is_array($fields)) {
3476:         $fields = array($fields);
3477:     }
3478:     if (!is_array($keywords)) {
3479:         $keywords = explode(' ', mb_convert_kana($keywords, 's'));
3480:     }
3481:     $str = '(1=1';
3482:     foreach ($keywords as $keyword) {
3483:         $keyword = addcslashes($keyword, '%_');
3484:         $str .= ' AND (1=0';
3485:         foreach ($fields as $field) {
3486:             if ($keyword === '' || $keyword === NULL) {
3487:                 continue;
3488:             }
3489:             $str .= ' OR ' . db_quote_field($field) . ' LIKE ';
3490:             $str .= "'%" . db_escape($keyword) . "%'";
3491:         }
3492:         $str .= ')';
3493:     }
3494:     $str .= ')';
3495:     sql_where($str);
3496: }
3497: 
3498: /**
3499:  * GROUP BY節を設定する
3500:  * 
3501:  * @param   string  $field  グループ化するフィールド名
3502:  * @package sql
3503:  */
3504: function sql_group($field)
3505: {
3506:     global $__photon_sql;
3507: 
3508:     $__photon_sql['group'] = db_quote_field($field);
3509: }
3510: 
3511: /**
3512:  * ORDER BY節を追加する
3513:  * 
3514:  * @param   string  $field      ソートするフィールド名
3515:  * @param   string  $ascending  昇順ならTRUE、降順ならFALSE
3516:  * @package sql
3517:  */
3518: function sql_order($field, $ascending = TRUE)
3519: {
3520:     global $__photon_sql;
3521: 
3522:     $str = db_quote_field($field);
3523:     if ($ascending === FALSE) {
3524:         $str .= ' DESC';
3525:     }
3526:     $str .= ',';
3527:     __sql_append('order', $str);
3528: }
3529: 
3530: /**
3531:  * LIMIT節を設定する
3532:  * 
3533:  * @param   integer $offset 取得開始する行番号
3534:  * @param   integer $limit  取得するレコード数
3535:  * @package sql
3536:  */
3537: function sql_limit($offset, $limit)
3538: {
3539:     global $__photon_sql;
3540: 
3541:     $__photon_sql['limit'] = intval($offset) . ',' . intval($limit);
3542: }
3543: 
3544: /**
3545:  * INSERT文のフィールド名と値を設定する
3546:  * 
3547:  * @param   string  $field  挿入するレコードのフィールド名
3548:  * @param   string  $value  挿入するレコードの値
3549:  * @package sql
3550:  */
3551: function sql_value($field, $value)
3552: {
3553:     __sql_append('insert_field', '`' . db_escape($field) . '`,');
3554:     __sql_append('insert_value', "'" . db_escape($value) . "',");
3555: }
3556: 
3557: /**
3558:  * UPDATE文のSET節に追加する
3559:  * 
3560:  * @param   string  $field  フィールド名
3561:  * @param   string  $expr   更新する式
3562:  * @package sql
3563:  */
3564: function sql_set($field, $expr)
3565: {
3566:     __sql_append('set', '`' . db_escape($field) . '`=' . $expr . ',');
3567: }
3568: 
3569: /**
3570:  * 文字列による更新をSET節に追加する
3571:  * 
3572:  * @param   string  $field  フィールド名
3573:  * @param   string  $value  更新する値
3574:  * @package sql
3575:  */
3576: function sql_set_string($field, $value)
3577: {
3578:     sql_set($field, "'" . db_escape($value) . "'");
3579: }
3580: 
3581: /**
3582:  * 整数値による更新をSET節に追加する
3583:  * 
3584:  * @param   string  $field  フィールド名
3585:  * @param   integer $value  更新する値
3586:  * @package sql
3587:  */
3588: function sql_set_integer($field, $value)
3589: {
3590:     sql_set($field, intval($value));
3591: }
3592: 
3593: /** @ignore */
3594: function __sql_append($key, $str)
3595: {
3596:     global $__photon_sql;
3597: 
3598:     if (!isset($__photon_sql[$key])) {
3599:         $__photon_sql[$key] = '';
3600:     }
3601:     $__photon_sql[$key] .= $str;
3602: }
3603: 
3604: //------------------------------------------------------------------------------
3605: // メール
3606: //------------------------------------------------------------------------------
3607: 
3608: /**
3609:  * メールを送信する
3610:  * 
3611:  * メールログの設定がある場合には、ログに記録する。
3612:  * 
3613:  * config関数で設定した下記の設定オプションが使用される。
3614:  * 
3615:  * |* log_mail  | メール送信履歴のテーブル名。空文字列で無効化
3616:  * 
3617:  * @param   string  $to         送信先メールアドレス
3618:  * @param   string  $from       送信元メールアドレス
3619:  * @param   string  $subject    メールの題名
3620:  * @param   string  $message    メールの本文
3621:  * @return  boolean 成功した場合にはTRUE、失敗した場合にはFALSE
3622:  * @package sendmail
3623:  */
3624: function sendmail($to, $from, $subject, $message)
3625: {
3626:     // 改行コードを変換
3627:     $to = convert_newline($to, '');
3628:     $from = convert_newline($from, '');
3629:     $subject = convert_newline($subject, '');
3630:     $message = convert_newline($message);
3631: 
3632:     // メールの送信
3633:     mb_send_mail($to, $subject, $message, 'From: ' . $from);
3634: 
3635:     // メール送信履歴に記録
3636:     $log_mail = config('log_mail');
3637:     if ($log_mail !== '') {
3638:         db_insert($log_mail, compact('to', 'from', 'subject', 'message'));
3639:     }
3640: }
3641: 
3642: //------------------------------------------------------------------------------
3643: // 認証
3644: //------------------------------------------------------------------------------
3645: 
3646: /**
3647:  * 認証領域を定義し、現在の認証領域に設定する
3648:  * 
3649:  * @param   string  $realm  認証領域の名前
3650:  * @param   string  $url    認証するためのログインURL
3651:  * @package auth
3652:  */
3653: function auth_realm($realm, $url)
3654: {
3655:     global $__photon_realm;
3656:     global $__photon_url;
3657:     global $__photon_id;
3658: 
3659:     $__photon_realm = $realm;
3660:     $__photon_url = $url;
3661: 
3662:     // 復号化
3663:     $key = config('secret_key');
3664:     if (isset($_COOKIE[$realm])) {
3665:         $str = base64_decode($_COOKIE[$realm]);
3666:         $str = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $str, MCRYPT_MODE_ECB);
3667:     } else {
3668:         $str = NULL;
3669:     }
3670: 
3671:     // ユーザ識別子を設定
3672:     $__photon_id = 0;
3673:     if ($str !== NULL) {
3674:         list($realm_check, $id, $time, ) = explode('|', $str);
3675:         if ($realm_check === $realm && is_numeric($id) && is_numeric($time)) {
3676:             $id = intval($id);
3677:             $time = intval($time);
3678:             $expire = $time + config('auth_expire');
3679:             if (time() < $expire) {
3680:                 $__photon_id = $id;
3681:                 auth_login($id);
3682:             }
3683:         }
3684:     }
3685: }
3686: 
3687: /**
3688:  * 現在の認証領域にログインする
3689:  * 
3690:  * @param   integer $id ユーザの識別子 (0以外の値)
3691:  * @package auth
3692:  */
3693: function auth_login($id)
3694: {
3695:     global $__photon_id;
3696:     global $__photon_realm;
3697: 
3698:     if (!isset($__photon_realm)) {
3699:         $__photon_realm = config('auth_realm');
3700:     }
3701: 
3702:     $__photon_id = $id;
3703: 
3704:     // 暗号化
3705:     $key = config('secret_key');
3706:     $random = openssl_random_pseudo_bytes(256);
3707:     $str = implode('|', array($__photon_realm, $id, time(), $random));
3708:     $str = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $str, MCRYPT_MODE_ECB);
3709:     $str = base64_encode($str);
3710:     setcookie($__photon_realm, $str);
3711: }
3712: 
3713: /**
3714:  * 現在の認証領域からログアウトする
3715:  * 
3716:  * @package auth
3717:  */
3718: function auth_logout()
3719: {
3720:     auth_login(0);
3721: }
3722: 
3723: /**
3724:  * 認証を要求する
3725:  * 
3726:  * 現在の認証領域でログインしていない場合には、auth_realmで設定した
3727:  * ログインURLにリダイレクトし、強制終了する。
3728:  * 
3729:  * @package auth
3730:  */
3731: function auth_require()
3732: {
3733:     global $__photon_url;
3734:     if (auth_id() == 0) {
3735:         redirect($__photon_url);
3736:     }
3737: }
3738: 
3739: /**
3740:  * ユーザの識別子を取得する
3741:  * 
3742:  * @return  integer ユーザの識別子。ログインしていない場合には、0を返す。
3743:  * @package auth
3744:  */
3745: function auth_id()
3746: {
3747:     global $__photon_id;
3748:     if (!isset($__photon_id)) {
3749:         // 認証領域が設定されていない場合の処理
3750:         auth_realm(config('auth_realm'), NULL);
3751:     }
3752:     return $__photon_id;
3753: }
3754: 
3755: //------------------------------------------------------------------------------
3756: // ビューとコントローラ
3757: //------------------------------------------------------------------------------
3758: 
3759: /**
3760:  * ブラウザをリダイレクトする
3761:  * 
3762:  * @param   string  $url    リダイレクト先のURL
3763:  * @package controller
3764:  */
3765: function redirect($url)
3766: {
3767:     header('Location: ' . relative_url($url, get_request_url()));
3768: }
3769: 
3770: /**
3771:  * 致命的エラーの発生を通知する
3772:  * 
3773:  * エラーメッセージを表示して、強制終了する。
3774:  * 
3775:  * config関数で設定した下記の設定オプションが使用される。
3776:  * 
3777:  * |* log_error     | エラーログのテーブル名。空文字列で無効化
3778:  * |* error_title   | エラーページのタイトル
3779:  * 
3780:  * @param   string  $message    エラーメッセージ
3781:  * @param   string  $severity   重要度
3782:  * @package controller
3783:  */
3784: function fatal($message, $severity = 'fatal')
3785: {
3786:     global $__photon_log_error;
3787: 
3788:     // トランザクションを終了する
3789:     db_rollback();
3790: 
3791:     // 出力バッファをクリア
3792:     ob_end_clean();
3793: 
3794:     // エラー履歴を記録
3795:     $log_error = config('log_error');
3796:     if ($log_error !== '') {
3797:         if (!isset($__photon_log_error)) {
3798:             $__photon_log_error = TRUE;
3799:             $url = get_request_url();
3800:             db_insert($log_error, compact('severity', 'url', 'message'));
3801:         }
3802:     }
3803: 
3804:     // エラーメッセージをブラウザに出力
3805:     if (is_true(ini_get('display_errors'))) {
3806:         $str = '<html>';
3807:         $str .= '<head>';
3808:         $str .= '<meta http-equiv="content-type" ';
3809:         $str .= 'content="text/html;charset=utf-8">';
3810:         $str .= '<title>' . config('error_title') . '</title>';
3811:         $str .= '</head>';
3812:         $str .= '<body>';
3813:         $str .= '<h1>' . config('error_title') . '</h1>';
3814:         $str .= '<p>' . htmlspecialchars($message) . '</p>';
3815:         $str .= '</body></html>';
3816:         die($str);
3817:     } else {
3818:         die();
3819:     }
3820: }
3821: 
3822: /**
3823:  * ビューのフィルタ関数を追加する
3824:  * 
3825:  * render関数の出力に対するフィルタ関数を登録する。
3826:  * 
3827:  * フィルタ関数は、第一引数が出力の文字列、第二引数が$argとなり、
3828:  * 変換結果を返り値とするものである。
3829:  * 
3830:  * フィルタ関数はチェイン処理され、
3831:  * ビュー評価の際に最初に登録したものから順番に実行される。
3832:  * 
3833:  * @param   string  $function   ビュー出力のフィルタ関数
3834:  * @param   mixed   $arg        フィルタ関数の第二引数
3835:  * @package controller
3836:  */
3837: function add_filter($function, $arg = NULL)
3838: {
3839:     global $__photon_filter;
3840:     $__photon_filter[] = array($function, $arg);
3841: }
3842: 
3843: /**
3844:  * ビューを評価し、ブラウザに出力する
3845:  * 
3846:  * $returnがTRUEの場合、ビューを出力せずに返り値として返す。
3847:  * ビューを評価する際、$dataの変数が展開される。
3848:  * 
3849:  * @param   string  $filename   ビューファイルのファイル名
3850:  * @param   array   $data       ビューに渡す変数
3851:  * @param   boolean $return     出力せずに返り値とするならTRUE
3852:  * @return  string  $returnがTRUEの場合にはビューファイルの評価結果
3853:  * @see     add_filter
3854:  * @package controller
3855:  */
3856: function render($filename, $data = NULL, $return = FALSE)
3857: {
3858:     global $__photon_filter;
3859: 
3860:     // ビューファイルの存在を確認する
3861:     if (!file_exists($filename)) {
3862:         fatal(config('error_view_not_found') . "\n" . $filename);
3863:     }
3864: 
3865:     // 変数を退避する
3866:     $__filename = $filename;
3867:     $__data = $data;
3868:     $__return = $return;
3869:     $__ob = $return || count($__photon_filter);
3870:     unset($data['__filename']);
3871:     unset($data['__data']);
3872:     unset($data['__return']);
3873:     unset($data['__ob']);
3874:     unset($data['__photon_filter']);
3875: 
3876:     if (!$__ob) {
3877:         // ビューファイルを出力する
3878:         extract($data);
3879:         include($filename);
3880:         return NULL;
3881:     }
3882: 
3883:     // ビューファイルを評価する
3884:     ob_start();
3885:     extract($data);
3886:     include($filename);
3887:     $contents = ob_get_contents();
3888:     ob_end_clean();
3889: 
3890:     // ビューファイルの評価結果をフィルタ処理を行う
3891:     foreach ($__photon_filter as $filter) {
3892:         list($function, $arg) = $filter;
3893:         $contents = call_user_func($function, $contents, $arg);
3894:     }
3895: 
3896:     if (!$__return) {
3897:         // 評価結果を出力する
3898:         echo $__return;
3899:         return NULL;
3900:     }
3901: 
3902:     // 評価結果を返す
3903:     return $__return;
3904: }
3905: 
3906: /**
3907:  * アクション関数を追加する
3908:  * 
3909:  * @param   string  $action     アクション名
3910:  * @param   string  $function   アクション関数
3911:  * @param   mixed   $arg        アクション関数の第二引数
3912:  * @package controller
3913:  */
3914: function add_action($action, $function, $arg = NULL)
3915: {
3916:     global $__photon_action;
3917:     $__photon_action[$action] = array($function, $arg);
3918: }
3919: 
3920: /**
3921:  * 現在のアクションを取得する
3922:  * 
3923:  * @param   array   $data   アクション関数のパラメータ
3924:  * @return  mixed   アクション名
3925:  * @package controller
3926:  */
3927: function get_action($data = NULL)
3928: {
3929:     // $dataが省略された場合、$_REQUESTをパラメータとする
3930:     if ($data === NULL) {
3931:         $data = $_REQUEST;
3932:     }
3933: 
3934:     // アクションを取得する
3935:     if (isset($data['action'])) {
3936:         if (is_array($data['action'])) {
3937:             return current(array_keys($data['action']));
3938:         } else {
3939:             return $data['action'];
3940:         }
3941:     } else {
3942:         return 'index';
3943:     }
3944: }
3945: 
3946: /**
3947:  * アクションを実行する
3948:  * 
3949:  * 通常は引数なしで呼び出し、GETやPOSTのactionパラメータのアクションを実行する。
3950:  * 
3951:  * action[アクション名]といった名前のボタンを作ることで、
3952:  * 複数のボタンでアクションを切り分ける事ができる。
3953:  * 
3954:  * $actionや$dataを指定することで、アクション内で別のアクションを呼び出すことも
3955:  * できる。
3956:  * 
3957:  * @param   string  $action     アクション名
3958:  * @param   mixed   $data       アクション関数のパラメータ
3959:  * @return  mixed   アクション関数の返り値
3960:  * @see     add_action
3961:  * @package controller
3962:  */
3963: function execute($action = NULL, $data = NULL)
3964: {
3965:     global $__photon_action;
3966: 
3967:     // $dataが省略された場合、$_REQUESTをパラメータとする
3968:     if ($data === NULL) {
3969:         $data = $_REQUEST;
3970:     }
3971: 
3972:     // $actionが省略された場合、$dataからアクション名を決定する
3973:     if ($action === NULL) {
3974:         $action = get_action($data);
3975:     }
3976: 
3977:     // アクションがadd_actionで追加されていた場合の処理
3978:     if (isset($__photon_action[$action])) {
3979:         list($function, $arg) = $__photon_action[$action];
3980:         return call_user_func($function, $data, $arg);
3981:     }
3982: 
3983:     // アクション関数が存在する場合
3984:     $function = 'action_' . $action;
3985:     if (function_exists($function)) {
3986:         return call_user_func($function, $data);
3987:     }
3988: 
3989:     // アクション関数が存在しない
3990:     fatal(config('error_action_not_found') . "\n" . $action);
3991: }
3992: 
3993: /** @ignore */
3994: function __photon_init()
3995: {
3996:     // PHPのバージョンを調べる
3997:     if (version_compare(PHP_VERSION, '4.4', '<=')) {
3998:         die('This application requires at least PHP version 4.4');
3999:     }
4000: 
4001:     // PHPの拡張を調べる
4002:     foreach (array('gd', 'mcrypt', 'mbstring') as $extension) {
4003:         if (!extension_loaded($extension)) {
4004:             die('This application requires extension: ' . $extension);
4005:         }
4006:     }
4007: 
4008:     // secret_keyが設定されているか調べる
4009:     if (config('secret_key') == '') {
4010:         die('The configuration file now needs a secret passphrase');
4011:     }
4012: 
4013:     // mbstringの設定
4014:     mb_language("Japanese");
4015:     mb_internal_encoding("utf-8");
4016: 
4017:     // ブラウザを閉じても実行する
4018:     ignore_user_abort();
4019: }
4020: 
4021: ?>
entap_photon API documentation generated by ApiGen 2.8.0