Overview

Classes

  • CMLLanguage
  • CMLPost
  • CMLTranslations
  • CMLUtils

Functions

  • cml_dropdown_langs
  • cml_get_browser_lang
  • cml_get_menu
  • cml_get_notice
  • cml_get_the_link
  • cml_is_homepage
  • cml_show_flags
  • Overview
  • Class
  • Tree
   1: <?php
   2: if ( ! defined( 'ABSPATH' ) ) die( "Access denied" );
   3: 
   4: /*
   5:  * Ceceppa Multilingua API 1.4
   6:  */
   7: 
   8:  /**
   9:   * This class provide information about configured languages
  10:   *
  11:   * @api
  12:   * 
  13:   * The structure of language object is:
  14:   * <p>
  15:   * stdClass Object (
  16:   * <ul>
  17:   *   <li>[id] => id of language</li>
  18:   *   <li>[cml_default] => ( boolean ) - is it default?</li>
  19:   *   <li>[cml_flag] => name of flag</li>
  20:   *   <li>[cml_language] => name of language</li>
  21:   *   <li>[cml_language_slug] => language slug</li>
  22:   *   <li>[cml_locale] => wordpress locale</li>
  23:   *   <li>[cml_enabled] => enabled?</li>
  24:   *   <li>[cml_sort_id] => language order</li>
  25:   *   <li>[cml_custom_flag] => (boolean) - use custom flag?</li>
  26:   *   <li>[cml_rtl] => 0</li>
  27:   *   <li>[cml_date_format] => j M Y</li>
  28:   * )
  29:   * </p>
  30:   */
  31: class CMLLanguage {
  32:   /** @ignore */
  33:   private static $_default_language;
  34:   /** @ignore */
  35:   private static $_all_languages; //All languages (enabled and not)
  36:   /** @ignore */
  37:   private static $_all_enabled;   //Only enabled languages
  38:   /** @ignore */
  39:   private static $_all_others;    //All language except default one
  40:   /** @ignore */
  41:   private static $_all_by_slug;   //All languages indexed by slug
  42:   /** @ignore */
  43:   private static $_current_id;    //Current language id
  44: 
  45:   /**
  46:    * Tiny ~= 16x11
  47:    */
  48:   const FLAG_TINY = "tiny";
  49: 
  50:   /**  
  51:    * Small ~= 32x21
  52:    */
  53:   const FLAG_SMALL = "small";
  54: 
  55:   /**
  56:    * return object of default language 
  57:    *
  58:    * @return stdObject
  59:    */
  60:   public static function get_default() {
  61:     if( empty( self::$_all_languages ) ) self::get_all();
  62: 
  63:     return self::$_default_language;
  64:   }
  65: 
  66:   /**
  67:    * return default language id
  68:    *
  69:    * @return int
  70:    */
  71:   public static function get_default_id() {
  72:     return self::get_default()->id;
  73:   }
  74: 
  75:   /**
  76:    * return default language slug
  77:    *
  78:    * return string
  79:    */
  80:   public static function get_default_slug() {
  81:     return self::get_default()->cml_language_slug;
  82:   }
  83:   
  84:   /**
  85:    * return all configured languages, enabled or not...
  86:    *
  87:    * @return stdObject
  88:    */
  89:   public static function get_all() {
  90:     global $wpdb;
  91:   
  92:     /*
  93:      * Prevent a lot of calls to database and store the result into this class
  94:      */
  95:     if( empty( self::$_all_languages) ) {
  96:       self::$_all_languages = $wpdb->get_results( sprintf( "SELECT * FROM %s ORDER BY cml_sort_id ", CECEPPA_ML_TABLE ) );
  97: 
  98:       $all_languages_by_keys = array();
  99:       $sorted = array();
 100:       $enableds = array();
 101:       $byslug = array();
 102:       $others = array();
 103:       foreach( self::$_all_languages as $l ) {
 104:         $all_languages_by_keys[ $l->id ] = $l;
 105:         $byslug[ $l->cml_language_slug ] = $l;
 106:         $sorted[] = $l;
 107: 
 108:         if( $l->cml_default == 1 )
 109:           self::$_default_language = $l;
 110:         else
 111:           $others[ $l->id ] = $l;
 112: 
 113:         if( $l->cml_enabled == 1 ) $enableds[$l->id] = $l;
 114:       }
 115:       
 116:       if( empty( self::$_default_language ) ) {
 117:         update_option( "cml_warning_no_default_language", true );
 118:         
 119:         self::$_default_language = end( $enableds );
 120:       }
 121: 
 122:       self::$_all_enabled = $enableds;
 123:       self::$_all_others = $others;
 124:       self::$_all_languages = $all_languages_by_keys;
 125:       self::$_all_by_slug = $byslug;
 126:       
 127:       if( ! empty( self::$_default_language ) )
 128:         self::$_current_id = self::$_default_language->id;
 129:     }
 130: 
 131:     return self::$_all_languages;
 132:   }
 133: 
 134:   /**
 135:    * return all configured languages except default one
 136:    *
 137:    * @return stdObject
 138:    */
 139:   public static function get_no_default() {
 140:     if( empty( self::$_all_languages) ) self::get_all();
 141:     
 142:     return self::$_all_others;
 143:   }
 144: 
 145:   /**
 146:    * return all enabled languages
 147:    *
 148:    * @return stdObject
 149:    */
 150:   public static function get_enableds() {
 151:     if( empty( self::$_all_languages ) ) self::get_all();
 152:     
 153:     return self::$_all_enabled;
 154:   }
 155: 
 156:   /*
 157:    * return all enabled languages except current one
 158:    *
 159:    * @param boolean $only_enableds return only enabled languages
 160:    * @return stdObject
 161:    */
 162:   public static function get_others( $only_enableds = true ) {
 163:     if( empty( self::$_all_languages ) ) self::get_all();
 164:     
 165:     $langs = ( $only_enableds ) ? self::$_all_enabled : self::$_all_languages;
 166:     unset( $langs[ self::get_current_id() ] );
 167:     
 168:     return $langs;
 169:   }
 170: 
 171:   /**
 172:    * return associative array where index
 173:    * is the language slug
 174:    *
 175:    * @example
 176:    * Array (
 177:    * <ul>
 178:    *   <li>"it" => [stdObject..],</li>
 179:    *   <li>"en" => [stdObject...]</li>
 180:    * </ul>
 181:    * )
 182:    *
 183:    * @return Array
 184:    */
 185:   public static function get_slugs() {
 186:     if( empty( self::$_all_languages ) ) self::get_all();
 187:     
 188:     return self::$_all_by_slug;
 189:   }
 190: 
 191:   /**
 192:    * return current language
 193:    *
 194:    * @return stdobject
 195:    */
 196:   public static function get_current() {
 197:     if( empty( self::$_all_languages ) ) self::get_all();
 198:     
 199:     return self::$_all_languages[ self::$_current_id ];
 200:   }
 201: 
 202:   /**
 203:    * return current language id
 204:    *
 205:    * @return int
 206:    */
 207:   public static function get_current_id() {
 208:     return self::get_current()->id;
 209:   }
 210: 
 211:   /**
 212:    * return current language slug
 213:    */
 214:   public static function get_current_slug() {
 215:     return self::get_current()->cml_language_slug;
 216:   }
 217: 
 218:   /**
 219:    * return the name of language
 220:    *
 221:    * @param int/string $lang - id or slug of language
 222:    *
 223:    * @return string
 224:    */
 225:   public static function get_name( $lang ) {
 226:     if( empty( self::$_all_languages ) ) self::get_all();
 227:     if( empty( self::$_default_language ) ) self::get_default();
 228:     if( ! is_numeric( $lang ) ) $lang = self::get_id_by_slug( $lang );
 229: 
 230:     return isset( self::$_all_languages[ $lang ] ) ? self::$_all_languages[ $lang ]->cml_language : "";
 231:   }
 232: 
 233:   /**
 234:    * return the slug of language
 235:    *
 236:    * @param int/string $lang - id or slug of language
 237:    *
 238:    * @return string
 239:    */
 240:   public static function get_slug( $lang ) {
 241:     if( empty( self::$_all_languages ) ) self::get_all();
 242:     if( empty( self::$_default_language ) ) self::get_default();
 243:     if( is_object( $lang ) ) $lang = $lang->id;
 244: 
 245:     if( $lang == 0 ) return self::$_default_language->cml_language_slug;
 246: 
 247:     return @self::$_all_languages[ $lang ]->cml_language_slug;
 248:   }
 249: 
 250:   /**
 251:    * return language by id
 252:    *
 253:    * @param int/string $lang id or slug of language to search
 254:    *
 255:    * @return stdObject
 256:    */
 257:   public static function get_by_id( $id ) {
 258:     if( empty( self::$_all_languages ) ) self::get_all();
 259:     if( ! is_numeric( $id ) ) $id = CMLLanguage::get_by_slug( $id );
 260: 
 261:     return self::$_all_languages[ $id ];
 262:   }
 263: 
 264:   /**
 265:    * get language by slug
 266:    *
 267:    * @param string $slug - language slug
 268:    * @param boolean $empty - If false return default language if $slug doesn't exists.
 269:    *                           If true and $slug doesn't exists return empty array
 270:    *
 271:    * @return stdObject
 272:    */
 273:   public static function get_by_slug( $slug, $empty = false ) {
 274:     if( empty( self::$_all_languages ) ) self::get_all();
 275: 
 276:     foreach( self::$_all_languages as $lang ) {
 277:       if( $lang->cml_language_slug == $slug ) return $lang;
 278:     }
 279:     
 280:     return ( ! $empty ) ? self::get_default() : array();
 281:   }
 282: 
 283:   /**
 284:    * return language id by slug
 285:    *
 286:    * @param string $slug - language slug
 287:    *
 288:    * @return int
 289:    */
 290:   public static function get_id_by_slug( $slug ) {
 291:     $lang = self::get_by_slug( $slug );
 292: 
 293:     return $lang->id;
 294:   }
 295: 
 296:   /**
 297:    * return language id by locale
 298:    *
 299:    * @param string $language locale
 300:    *
 301:    * @return int
 302:    */
 303:   public static function get_id_by_locale( $locale ) {
 304:     $langs = self::get_all();
 305:     
 306:     foreach( $langs as $lang ) {
 307:       if( $lang->cml_locale == $locale ) return $lang;
 308:     }
 309:     
 310:     return null;
 311:   }
 312: 
 313:   /**
 314:    * return the flag filename ( withouth extension )
 315:    *
 316:    * @param int/string $lang ( optional ) - id/slug of language, if empty default one will be used
 317:    * @param string $size ( optional ) - size of flag: "tiny" or "small"
 318:    *
 319:    * @return string
 320:    */
 321:   public static function get_flag( $lang = null, $size = "small" ) {
 322:     if( empty( $lang ) ) $lang = self::default_language_id();
 323:     if( ! is_numeric( $lang ) ) $lang = self::get_id_by_slug( $lang );
 324: 
 325:     if( empty( self::$_all_languages ) ) self::get_all();
 326: 
 327:     return self::$_all_languages[ $lang ]->cml_flag;
 328:   }
 329: 
 330:   /**
 331:    * return flag filename with the full path.
 332:    *
 333:    * Example
 334:    * www.example.com/wp-content/plugin/ceceppa-multilingua/flags/tiny/it_IT.png
 335:    *
 336:    * @param int/string $lang ( optional ) - id or slug of language, if empty default one will be used
 337:    * @param string $size ( optional ) - size of flag: "tiny" or "small"
 338:    *
 339:    * @return string: flag filename with the full path.
 340:    */
 341:   public static function get_flag_src( $lang = null, $size = CML_FLAG_TINY ) {
 342:     if( empty( self::$_all_languages ) ) self::get_all();
 343:     if( empty( self::$_default_language ) ) self::get_default();
 344:     if( $lang == null ) $lang = self::get_default_id();
 345:     if( ! is_numeric( $lang ) ) $lang = self::get_id_by_slug( $lang );
 346: 
 347:     $lang = self::$_all_languages[ $lang ];
 348:     $flag = $lang->cml_flag;
 349: 
 350:     if( $lang->cml_custom_flag == 1 && file_exists( CML_UPLOAD_DIR . "/$size/$flag.png" ) )
 351:       $url = CML_UPLOAD_URL . "/$size/$flag.png";
 352:     else
 353:       $url = CML_PLUGIN_URL . "flags/$size/$flag.png";
 354: 
 355:     return esc_url( $url );
 356:   }
 357:   
 358:   /**
 359:    * return html <img> object of flag
 360:    *
 361:    * @param int/string $lang - id or slug of language
 362:    * @param string $size - flag size ( tiny or small )
 363:    *
 364:    * @return string
 365:    */
 366:   public static function get_flag_img( $lang, $size = CML_FLAG_TINY ) {
 367:     $url = self::get_flag_src( $lang, $size );
 368:     $name = self::get_name( $lang );
 369:     $slug = self::get_slug( $lang );
 370: 
 371:     return "<img src='$url' border='0' alt='$slug' title='$name' />";
 372:   }
 373:    
 374:    /**
 375:     * @ignore
 376:     *
 377:     * force "reload" languages
 378:     */
 379:    public static function reload() {
 380:     self::$_all_languages = null;
 381:    }
 382: 
 383:   /**
 384:    * get language object by post id
 385:    *
 386:    * @param int $post_id - id of post/page
 387:    *
 388:    * @return stdObject
 389:    */
 390:   public static function get_by_post_id( $post_id ) {
 391:     return CMLPost::get_language_by_id( $post_id );
 392:   }
 393: 
 394:   /**
 395:    * get language id by post id
 396:    *
 397:    * @param int $post_id - id of post
 398:    *
 399:    * @return int
 400:    */
 401:   public static function get_id_by_post_id( $post_id ) {
 402:     return CMLPost::get_language_id_by_id( $post_id );
 403:   }
 404:   
 405:   /**
 406:    * Is $lang the default one?
 407:    *
 408:    * @param int/string $lang ( optional ) id/slug. check if $lang is the default language, if null is passed
 409:    *                        current language will be assumed
 410:    * @return boolean
 411:    */
 412:   public static function is_default( $lang = null) {
 413:     if( null == $lang ) {
 414:       $lang = CMLLanguage::get_current_id();
 415:     } else {
 416:       if( ! is_numeric( $lang ) ) {
 417:         $lang = CMLLanguage::get_id_by_slug( $lang );
 418:       }
 419:     }
 420: 
 421:     return $lang == CMLLanguage::get_default_id();
 422:   }
 423:  
 424:   /**
 425:    * check if $lang is the current language
 426:    *
 427:    * @param int/string $lang language id/slug to compare
 428:    *
 429:    * @return boolean
 430:    */
 431:   public static function is_current( $lang ) {
 432:     if( null == $lang ) {
 433:       $lang = CMLLanguage::get_current_id();
 434:     } else {
 435:       if( ! is_numeric( $lang ) ) {
 436:         $lang = CMLLanguage::get_id_by_slug( $lang );
 437:       }
 438:     }
 439:     
 440:     return $lang == CMLLanguage::get_current_id();
 441:   }
 442: 
 443:   /**
 444:    * @ignore
 445:    * 
 446:    * set current language
 447:    *
 448:    * @param $lang - language object or id
 449:    *
 450:    * @return void
 451:    */
 452:   public static function set_current( $lang ) {
 453:     $id = is_object( $lang ) ? $lang->id : $lang;
 454:     
 455:     self::$_current_id = $id;
 456:   }
 457: }
 458: 
 459: /**
 460:  * This class is used to get and store custom translations in CECEPPA_ML_TRANSLATIONS table.
 461:  */
 462: class CMLTranslations {
 463:   //store "get" translations
 464:   /** @ignore */
 465:   private static $_translations = array();
 466:   /** @ignore */
 467:   private static $_keys = array();
 468: 
 469:   /**
 470:    * This function can be used by 3rd part plugin/theme to allow translation of its strings.
 471:    * Added string can be translated in "My Languages" page.
 472:    *
 473:    * @example
 474:    * Full example is provided here:<br />
 475:    *  http://www.alessandrosenese.eu/en/ceceppa-multilingua/extend-plugin-theme-compatibility/
 476:    *
 477:    * @param string $key used to store/get your own value from database. To avoid duplicated key use your own
 478:    *                        prefix for your key. Example: "_yoast_rssafter"
 479:    * @param string $default default value to use
 480:    * @param string $group The group name of your own strings, this name will be displayed in "My Translations" page
 481:    *                      in "Group" column. Group name should be preceded by "_" symbol.<br />
 482:    *                      Example: "_YOAST"
 483:    *
 484:    * @return void
 485:    */
 486:   public static function add( $key, $default, $group ) {
 487:     global $wpdb;
 488: 
 489:     $default = bin2hex( $default );
 490:     foreach( CMLLanguage::get_all() as $lang ) {
 491:       $query = sprintf( "SELECT id FROM %s WHERE cml_text = '%s' AND cml_lang_id = %d AND cml_type = '%s'",
 492:                         CECEPPA_ML_TRANSLATIONS,
 493:                         bin2hex( strtolower( $key ) ),
 494:                         $lang->id,
 495:                         $group );
 496:   
 497:       $record = $wpdb->get_var( $query );
 498:       if( empty( $record ) ) {
 499:         $wpdb->insert( CECEPPA_ML_TRANSLATIONS,
 500:                       array(
 501:                             'cml_text' => bin2hex( strtolower( $key ) ),
 502:                             'cml_lang_id' => $lang->id,
 503:                             'cml_translation' => $default,
 504:                             'cml_type' => $group
 505:                             ),
 506:                       array(
 507:                         '%s', '%d', '%s', '%s',
 508:                       )
 509:                      );
 510:       }
 511:     }
 512:   }
 513:   
 514:   /**
 515:    * Store custom translation in database.
 516:    *
 517:    * Since 1.4 CML generate GNUTEXT mo file from stored translations. 
 518:    * The domain used to generate translation is: "cmltrans".
 519:    *
 520:    * Mo file isn't generated automatically, but you have to call manually
 521:    * the function cml_generate_mo_from_translations()
 522:    * 
 523:    * This function will return the id of inserted record.
 524:    * 
 525:    * @example
 526:    *
 527:    * <?php _e( "Hello", "cmltrans" ); ?>
 528:    *
 529:    * Use "get" function instead of __, _e, because if "mo" generation fails,
 530:    * this function will get translation from database
 531:    * 
 532:    * @param int/string $lang - id/slug of language
 533:    * @param string $original - original string
 534:    * @param string $translation - translated string
 535:    * @param string $type - type of translation( W: Widget, S: Custom strings ).
 536:    *                Type field is used "internally" to show records in correct table, like
 537:    *                Widget titles, My translations...
 538:    *
 539:    * @return string
 540:    */
 541:   public static function set( $lang, $original, $translated, $type ) {
 542:     global $wpdb;
 543: 
 544:     if( ! is_numeric( $lang ) ) $lang = CMLLanguage::get_id_by_slug( $lang );
 545:     $original = trim( $original );
 546: 
 547:     $wpdb->delete( CECEPPA_ML_TRANSLATIONS,
 548:                     array( "cml_text" => bin2hex( $original ),
 549:                            "cml_type" => $type,
 550:                            "cml_lang_id" => $lang ),
 551:                     array( "%s", "%s", "%d" ) );
 552: 
 553:     return $wpdb->insert( CECEPPA_ML_TRANSLATIONS, 
 554:                           array( 'cml_text' => bin2hex( $original ),
 555:                                 'cml_lang_id' => $lang,
 556:                                 'cml_translation' => bin2hex( $translated ),
 557:                                 'cml_type' => $type ),
 558:                           array( '%s', '%d', '%s', '%s' ) );
 559:   }
 560:   
 561:   /**
 562:    * return translation stored in cml_trans table.
 563:    *
 564:    * This function will get translation from ".mo" file if:
 565:    *    1) it's generated correctly
 566:    *    2) $lang == current language
 567:    *
 568:    * otherwise will get translation from database
 569:    *
 570:    * <strong>string match is case sensitive</strong>
 571:    * 
 572:    * @param int/string $lang - language id or slug
 573:    * @param string $string - string to translate
 574:    * @param string $type - ( optional ) This is used internally by Ceceppa Multilingua, only in admin interface
 575:    *                  T - Site Title/Tagline,
 576:    *                  W - Widget,
 577:    *                  M - My translations
 578:    *                  
 579:    * @param boolean $return_empty - If true, return empty string if no translation is found
 580:    * @param boolean $ignore_po  Since 1.4 the plugin will generate mo file for all translation stored
 581:    *                                in CECEPPA_ML_TRANSLATIONS ( widget titles, my translations, site title/tagline... ).
 582:    *                                So by default if $lang == current language, the plugin will get translation
 583:    *                                from ".mo" file instead query the database.
 584:    *                                You can force to retive translation from database.
 585:    *
 586:    * @return string                 
 587:    */
 588:   public static function get( $lang, $string, $type = "", $return_empty = false, $ignore_po = false ) {
 589:     global $wpdb;
 590: 
 591:     if( "_" == $type[ 0 ] && ! $return_empty ) {
 592:       $return_empty = true;
 593:     }
 594: 
 595:     $string = trim( $string );
 596: 
 597:     //C = Category
 598:     $s = ( $type == "C" ) ? strtolower( $string ) : $string;
 599: 
 600:     //Look if I already translated it...
 601:     if( isset( self::$_keys[ $lang ] ) && in_array( sanitize_title( $s ), self::$_keys[ $lang ] ) ) {
 602:       $index = array_search( sanitize_title( $s ), self::$_keys[ $lang ] );
 603:       return self::$_translations[ $lang ][ $index ];
 604:     }
 605: 
 606:     if( CML_GET_TRANSLATIONS_FROM_PO &&
 607:        ! $ignore_po &&
 608:        1 == CMLUtils::_get( '_po_loaded' ) ) {
 609:          //file_exists( CML_PLUGIN_CACHE_PATH . "cmltrans-" . CMLLanguage::get_current()->cml_locale . ".mo" ) ) {
 610:       $ret = __( $s, 'cmltrans' );
 611:     }
 612: 
 613:     if( ! is_numeric( $lang ) ) {
 614:       $lang = CMLLanguage::get_id_by_slug( $lang );
 615:     }
 616: 
 617:     $query = sprintf(" SELECT UNHEX(cml_translation) FROM %s WHERE cml_text = '%s' AND cml_lang_id = %d AND cml_type LIKE '%s'",
 618:                                     CECEPPA_ML_TRANSLATIONS, bin2hex( $s ), $lang, "%" . $type . "%" );
 619: 
 620:     $return = $wpdb->get_var( $query );
 621: 
 622:     if( $return_empty && empty( $return ) ) return "";
 623: 
 624:     $return = ( empty( $return ) ) ?  $string : html_entity_decode( stripslashes( $return ) );
 625:     
 626:     self::$_translations[$lang][] = $return;
 627:     self::$_keys[ $lang ][] = sanitize_title( $string );
 628: 
 629:     return $return;
 630:   }
 631:   
 632:   /*
 633:    * get translation from wordpress
 634:    *
 635:    * @ignore
 636:    */
 637:   public static function gettext( $lang, $string, $type, $ignore_po = false ) {
 638:     $ret = self::get( $lang, $string, $type, true, $ignore_po );
 639: 
 640:     if( ! empty( $ret ) ) return $ret;
 641: 
 642:     //Recupero la traduzione dalle frasi di wordpress ;)
 643:     require_once( CML_PLUGIN_PATH . "gettext/gettext.inc" );
 644:     
 645:     $locale = CMLLanguage::get_current()->cml_locale;
 646: 
 647:     // gettext setup
 648:     T_setlocale( LC_MESSAGES, $locale );
 649:     // Set the text domain as 'messages'
 650: 
 651:     $domain = $locale;
 652:     T_bindtextdomain( $domain, CML_WP_LOCALE_DIR );
 653:     T_bind_textdomain_codeset( $domain, 'UTF-8' );
 654:     T_textdomain( $domain );
 655: 
 656:     $ret = T_gettext( $string );
 657: 
 658:     return ( empty( $ret ) ) ?  $string : html_entity_decode( stripslashes( $ret ) );
 659:   }
 660:   
 661:   /**
 662:    * delete all records with cml_type = xx
 663:    *
 664:    * @ignore
 665:    */
 666:   public static function delete( $type ) {
 667:     global $wpdb;
 668:     
 669:     $wpdb->delete( CECEPPA_ML_TRANSLATIONS,
 670:                   array( "cml_type" => $type ),
 671:                   array( "%s" ) );
 672:   }
 673: }
 674: 
 675: /**
 676:  * This class is used to get/set post translation/language or get language by its id
 677:  * 
 678:  */
 679: class CMLPost {
 680:   /** @ignore */
 681:   private static $_indexes = null;
 682:   /** @ignore */
 683:   private static $_posts_meta = array();
 684: 
 685:   /**
 686:    * return language object by post id
 687:    *
 688:    * this function return "null" if post doesn't exists in any language
 689:    * 
 690:    * @param int/string $post_id - id of post/page
 691:    *
 692:    * @return stdObject
 693:    */
 694:   public static function get_language_by_id( $post_id ) {
 695:     if( empty( self::$_indexes ) ) self::_load_indexes();
 696: 
 697:     /*
 698:      * search in current language first.
 699:      * because if post exists in multiple languages, included current one,
 700:      * I have to return current language id, not random :)
 701:      */
 702:     if( @in_array( $post_id, self::$_indexes[ CMLLanguage::get_current_id() ] ) ) {
 703:       return CMLLanguage::get_current();
 704:     }
 705: 
 706:     foreach( CMLLanguage::get_others() as $lang ) {
 707:       if( @in_array( $post_id, self::$_indexes[ $lang->id ] ) )
 708:         return $lang;
 709:     }
 710: 
 711:     return null;
 712:   }
 713:   
 714:   /**
 715:    * return language id by post id
 716:    *
 717:    * @param int $post_id - id of post/page
 718:    * @param boolean $meta - default false.
 719:    *                        If true use wp function "get_post_meta", instead of get_language_by_id method
 720:    *                        to get language of post.
 721:    *                        In backend I need to get information by post meta, or I'll lost
 722:    *                        "all languages" ( = 0 ) information.
 723:    *
 724:    * @return int
 725:    */
 726:   public static function get_language_id_by_id( $post_id, $meta = false ) {
 727:     if( $meta ) {
 728:       //Get from meta
 729:       $meta = get_post_meta( $post_id, "_cml_meta", true );
 730:       
 731:       if( ! empty( $meta ) && isset( $meta[ "lang" ] ) ) {
 732:         return $meta[ "lang"];
 733:       }
 734:     }
 735: 
 736:     $lang = self::get_language_by_id( $post_id );
 737: 
 738:     if( null === $lang ) {
 739:       //Get from meta
 740:       $meta = get_post_meta( $post_id, "_cml_meta", true );
 741:       
 742:       if( empty( $meta ) ) {
 743:         $lang = self::get_language_by_id( $post_id );
 744:   
 745:         if( is_object( $lang ) ) {
 746:           //update meta
 747:           self::update_meta( $lang->id, $post_id );
 748:         }
 749:   
 750:         return is_object( $lang ) ? $lang->id : 0;
 751:       } else {
 752:         return @$meta[ "lang" ];
 753:       }
 754:     }
 755:     
 756:     return $lang->id;
 757:   }
 758:   
 759:   /**
 760:    * get language slug by post id
 761:    *
 762:    * @param int $post_id post/page id
 763:    *
 764:    * @return string
 765:    */
 766:   public static function get_language_slug_by_id( $post_id ) {
 767:     $lang = self::get_language_by_id( $post_id );
 768:     
 769:     return is_object( $lang ) ? $lang->cml_language_slug : "";
 770:   }
 771: 
 772:   /**
 773:    * get the translation id, if exists, in selected language
 774:    *
 775:    * this function will return 0 if no translation is found.
 776:    * 
 777:    * @param int/string $lang - language id/slug in which return translation
 778:    * @param int $post_id - post id
 779:    *
 780:    * @return int
 781:    */
 782:   public static function get_translation( $lang, $post_id ) {
 783:     global $wpdb;
 784: 
 785:     if( is_numeric( $lang ) ) $lang = CMLLanguage::get_slug( $lang );
 786:     if( empty( $post_id ) ) return 0;
 787: 
 788:     //if( ! CECEPPA_ML_MIGRATED ) {
 789:     //  return cml_old_get_linked_post( $wpCeceppaML->get_language_id_by_post_id( $post_id ), null, $post_id, $lang );
 790:     //}
 791:   
 792:     $linked = self::get_translations( $post_id );
 793: 
 794:     return ( ! array_key_exists( $lang, $linked) ) ? 0 : $linked[ $lang ];
 795:   }
 796: 
 797:   /**
 798:    * get all available translations of post
 799:    *
 800:    * This function will return Array containing all info about linked posts
 801:    *
 802:    * Array(
 803:    * <ul>
 804:    *   <li>[language slug] => [post_id]</li>
 805:    *   <li>...</li>
 806:    *   <li>[indexes] => Array<br />
 807:    *    <i>In this subarray there are all linked posts, including $post_id</i>
 808:    *    <ul>
 809:    *      <li>[language slug] => [post_id]</li>
 810:    *      <li>...</li>
 811:    *    </ul>
 812:    *   </li>
 813:    *   <li>
 814:    *    [linked] => Array<br />
 815:    *    <i>In this subarray there are only linked post indexes</i> 
 816:    *    <ul>
 817:    *      <li>[linked language slug] => [linked_id]</li>
 818:    *      <li>...</li>
 819:    *    </ul>
 820:    *  </li>
 821:    * </ul>
 822:    * )
 823:    * 
 824:    * @example
 825:    * <br />
 826:    * Array (
 827:    * <ul>
 828:    *   <li>[it] => 2552</li>
 829:    *   <li>[en] => 541</li>
 830:    *   <li>[eo] => 0</li>
 831:    *   <li>[indexes] => Array
 832:    *       (
 833:    *       <ul>
 834:    *        <li>[it] => 2552</li>
 835:    *       </ul>
 836:    *       )
 837:    *    </li>
 838:    *    <li>
 839:    *   [others] => Array
 840:    *       (
 841:    *       <ul>
 842:    *        <li>[en] => 541</li>
 843:    *       )
 844:    *    </li>
 845:    *  </ul>
 846:    * )
 847:    *
 848:    * @param int $post_id - post id
 849:    * @param boolean $force - force to rebuild meta. ( This parameter is used internally by CML )
 850:    *
 851:    * return Array
 852:    */
 853:   public static function get_translations( $post_id, $force = false ) {
 854:     global $wpdb;
 855:   
 856:     if( empty( $post_id ) ) return array();
 857: 
 858:     if( ! isset( self::$_posts_meta[ $post_id ] ) || $force ) {
 859:       //if( ! CECEPPA_ML_MIGRATED ) return cml_old_get_linked_posts( $id );
 860:       if( empty( $GLOBALS[ '_cml_language_columns' ] ) ) {
 861:         require_once ( CML_PLUGIN_ADMIN_PATH . 'admin-settings-gen.php' );
 862: 
 863:         cml_generate_lang_columns();
 864:       }
 865:     
 866:       $_cml_language_columns = & $GLOBALS[ '_cml_language_columns' ];
 867:       $_conv = & $GLOBALS[ '_cml_language_keys' ];
 868: 
 869:       $query = "SELECT ";
 870:       foreach( $_conv as $key => $label ) {
 871:         $select[] = "$key as $label";
 872:       }
 873:       $query .= join( ",", $select ) . " FROM " . CECEPPA_ML_RELATIONS . " WHERE ";
 874:       foreach( $_cml_language_columns as $l ) {
 875:         $where[] = "$l = $post_id";
 876:       }
 877:       $query .= join( " OR ", $where );
 878:     
 879:       $row = $wpdb->get_row( $query, ARRAY_A );
 880:       unset( $row[ "id" ] );
 881: 
 882:       $keys = @array_filter( $row );
 883:       $keys = @array_replace( $keys, $_conv );
 884:       $others = @array_filter( is_array( $row ) ? $row : array() );
 885:       unset( $others[ CMLPost::get_language_slug_by_id( $post_id ) ] );
 886:     
 887:       $row = @array_merge( (array) $row, array( "indexes" => array_filter( $row ),
 888:                                                "linked" => $others ) );
 889:       
 890:       self::$_posts_meta[ $post_id ] = $row;
 891:     } else {
 892:       $row = self::$_posts_meta[ $post_id ];
 893:     }
 894: 
 895:     return $row;
 896:   }
 897:   
 898:   /**
 899:    * set language of post
 900:    *
 901:    * This function will unlink $post_id by its translations
 902:    * 
 903:    * @param int/string $lang - post language id/slug 
 904:    * @param int $post_id - post id
 905:    */
 906:   public static function set_language( $lang, $post_id ) {
 907:     if( ! is_numeric( $lang ) ) $lang = CMLLanguage::get_id_by_slug( $lang );
 908: 
 909:     cml_migrate_database_add_item( $lang, $post_id, 0, 0 );
 910:   }
 911: 
 912:   /**
 913:    * set single translation to post id
 914:    *
 915:    * This function is used to link 2 posts
 916:    *
 917:    * When you link a post to single translation, relations with other language will not be losed.
 918:    * If you want remove other relations, you have to use set_language method first.
 919:    * 
 920:    * @param int $post_id - post to set translation
 921:    * @param int/string $linked_lang - language id/slug of linked post
 922:    * @param int $linked_post - post id of translation
 923:    * @param int $post_lang ( optional ) - language of $post_id. If null, It will get from database
 924:    *
 925:    * @return void
 926:    */
 927:   public static function set_translation( $post_id, $linked_lang, $linked_post, $post_lang = null ) {
 928:     self::set_translations( $post_id, array( $linked_lang => $linked_post ), $post_lang );
 929:   }
 930: 
 931:   /**
 932:    * add multiple translations to post id
 933:    *
 934:    * This function will update relations only from $post_id with $translations posts, so relations
 935:    * from $post_id and languages than doesn't exists in $translations array will not be broken.
 936:    * 
 937:    * If you need to set relation only from $post_id and $translations, and remove the other one, you
 938:    * have to "break" them using set_language method first.
 939:    * 
 940:    * @param $post_id - post to set translation+
 941:    * @param $translations - array with language_slug as key and post_id as value.
 942:    *                        array( "it" => 1, "en" => 2 )...
 943:    * @param $post_lang ( optional ) - set also the language of $post_id
 944:    *
 945:    * @example:
 946:    *
 947:    *    CMLPost::set_translation( 1, array( "it" => 2, "en" => 3 ) )
 948:    */
 949:   public static function set_translations( $post_id, $translations, $post_lang = null ) {
 950:     //$_cml_language_columns = & $GLOBALS[ '_cml_language_columns' ];
 951:     if( null === $post_lang ) $post_lang = CMLPost::get_language_id_by_id( $post_id );
 952: 
 953:     foreach( $translations as $key => $id ) {
 954:       if( ! is_numeric( $key ) ) $key = CMLLanguage::get_by_slug( $key );
 955: 
 956:       cml_migrate_database_add_item( $post_lang, $post_id, $key, $id );
 957:     }
 958: 
 959:     //Update info
 960:     cml_fix_rebuild_posts_info();
 961:     self::_load_indexes();
 962:     
 963:     self::update_meta( $post_lang, $post_id );
 964:   }
 965:   
 966:   /*
 967:    * update post meta
 968:    * @ignore
 969:    */
 970:   public static function update_meta( $lang, $post_id ) {
 971:     /*
 972:      * I just updated post relation, so I have to rebuild meta :)
 973:      */
 974:     //Add my meta to post
 975:     $meta = array( "lang" => $lang,
 976:                     "translations" => CMLPost::get_translations( $post_id, true ) );
 977: 
 978:     update_post_meta( $post_id, "_cml_meta", $meta );
 979:   }
 980: 
 981:   /**
 982:    * get indexes of posts that exists in selected language
 983:    *
 984:    * @param int/slug $lang ( optional, if not set will be = current language id )
 985:    *                      id / slug of language
 986:    *
 987:    * @return array
 988:    */
 989:   public static function get_posts_by_language( $lang = null ) {
 990:     if( empty( $lang ) ) $lang = CMLLanguage::get_current_id();
 991:     if( ! is_numeric( $lang ) ) $lang = CMLLanguage::get_id_by_slug( $lang );
 992:   
 993:     //Gli articoli senza lingua sono "figli di tutti"
 994:     if( empty( self::$_indexes ) ) self::_load_indexes();
 995: 
 996:     $posts = @self::$_indexes[ $lang ];
 997: 
 998:     return ! empty( $posts ) ? array_unique( $posts ) : array();
 999:   }
1000: 
1001:   /*
1002:    * for private use only, update post indexes by language
1003:    * @ignore
1004:    */
1005:   public static function _update_posts_by_language( $lang, $ids ) {
1006:     self::$_indexes[ $lang ] = $ids;
1007:   }
1008: 
1009:   /**
1010:    * return all posts by languages.
1011:    *
1012:    * The key of array is the language id
1013:    *
1014:    * @return array
1015:    */
1016:   public static function get_posts_by_languages() {
1017:     //Gli articoli senza lingua sono "figli di tutti"
1018:     if( empty( self::$_indexes ) ) self::_load_indexes();
1019: 
1020:     return self::$_indexes;
1021:   }
1022: 
1023:   /**
1024:    * check if $post_id has translation in selected language.
1025:    *
1026:    * @param int/string $lang - language id/slug
1027:    * @param int $post_id - post id
1028:    * 
1029:    * return boolean
1030:    */
1031:   public static function has_translation( $lang, $post_id ) {
1032:     if( ! isset( self::$_posts_meta[ $post_id ] ) ) {
1033:       self::$_posts_meta[ $post_id ] = self::get_translations( $post_id );
1034:     }
1035: 
1036:     if( is_numeric( $lang ) ) $lang = CMLLanguage::get_slug( $lang );
1037: 
1038:     return ( isset( self::$_posts_meta[ $post_id ]['indexes'] ) &&
1039:             self::$_posts_meta[ $post_id ]['indexes'][ $lang ] > 0 );
1040:   }
1041: 
1042:   /**
1043:    * check if $post_id has any translation
1044:    *
1045:    * @param int $post_id - post id
1046:    *
1047:    * @return boolean
1048:    */
1049:   public static function has_translations( $post_id ) {
1050:     if( ! isset( self::$_posts_meta[ $post_id ] ) ) {
1051:       self::$_posts_meta[ $post_id ] = self::get_translations( $post_id );
1052:     }
1053: 
1054:     return ! empty( self::$_posts_meta[ $post_id ][ 'linked' ] );
1055:   }
1056: 
1057:   /**
1058:    * check if $post1 is translation of $post2
1059:    *
1060:    * @param int $post1 post id
1061:    * @param int $post2 post id
1062:    *
1063:    * @return boolean
1064:    */
1065:   public static function is_translation( $post1, $post2 ) {
1066:     $translations = CMLPost::get_translations( $post1 );
1067: 
1068:     return in_array( $post2, $translations[ 'indexes' ] );
1069:   }
1070: 
1071:   /** @ignore */
1072:   private static function _load_indexes() {
1073:     $langs = cml_get_languages( false );
1074: 
1075:     foreach($langs as $lang) {
1076:       self::$_indexes[ $lang->id ] = get_option( "cml_posts_of_lang_" . $lang->id );
1077:     }
1078:   }
1079: 
1080:   /**
1081:    * @ignore
1082:    *
1083:    * Remove extra "-##" add by wordpress when more than one post has
1084:    * same titles, but ONLY on translations.
1085:    */
1086:   public static function remove_extra_number( $permalink, $post ) {
1087:     global $wpdb;
1088: 
1089:     $removed = false;
1090: 
1091:     if( is_object( $post ) && CMLPost::has_translations( $post->ID ) ) {
1092:       //Remove last "/"
1093:       $url = untrailingslashit( $permalink );
1094:       $url = str_replace( CMLUtils::home_url(), "", $url );
1095:   
1096:       /*
1097:        * Post/page link contains "-d"
1098:        */
1099:       preg_match_all( "/-\d+/", $url, $out );
1100:   
1101:       /*
1102:        * if true I have to check if it was added by "wordpress" :)
1103:        */
1104:       if( count( $out[ 0 ] ) > 0 ) {
1105:         /*
1106:          * when hook get_page_link, wordpress pass me only post id, not full object
1107:          */
1108:         $post_title = $post->post_title; //( ! isset( $post->post_name ) ) ?
1109:           //$post->post_title : $post->post_name;
1110:           // $a = $wpdb->get_var( "SELECT post_title FROM $wpdb->posts WHERE id = $post->ID" );
1111: 
1112:         /*
1113:          * got how many number occourrences ( -d ) are in the "real title"
1114:          */
1115:         preg_match_all( "/\d+/", $post_title, $pout );
1116: 
1117:         /*
1118:          * compare occourrences between permalink and title,
1119:          * if title contains more one, I remove it :)
1120:          */
1121:         //Remove autoinserted -## from url
1122:         if( count( $pout[0] ) < count( $out[ 0 ] ) ) {
1123:           $permalink = trailingslashit( preg_replace( "/-\d*$/", "",
1124:                                                      untrailingslashit( $permalink ) ) );
1125: 
1126:           $removed = true;
1127:         }
1128:       }
1129: 
1130:       if( $removed &&
1131:           CMLUtils::get_url_mode() == PRE_NONE ) {
1132:         $post_id = is_object( $post ) ? $post->ID : $post;
1133: 
1134:         $lang = CMLLanguage::get_by_post_id( $post_id );
1135:         if( empty( $lang ) ) {
1136:           $lang = CMLLanguage::get_current();
1137:         }
1138: 
1139:         $permalink = add_query_arg( array(
1140:                                           "lang" => $lang->cml_language_slug,
1141:                                           ), $permalink );
1142:       }
1143:     }
1144: 
1145:     return $permalink;
1146:   }
1147: }
1148: 
1149: /**
1150:  * utility class
1151:  *
1152:  */
1153: class CMLUtils {
1154:   /** @ignore */
1155:   private static $_permalink_structure;
1156:   /** @ignore */
1157:   private static $_url_mode;
1158:   /** @ignore */
1159:   private static $_date_format;
1160:   /** @ignore */
1161:   private static $_home_url;
1162:   /** @ignore */
1163:   private static $_clean_url;
1164:   /** @ignore */
1165:   private static $_clean_request;
1166:   /** @ignore */
1167:   private static $_request_url;
1168:   /** @ignore */
1169:   private static $_clean_applied = false;
1170:   /** @ignore */
1171:   private static $_url;
1172:   /** @ignore */
1173:   private static $_language_detected = null;
1174:   /** @ignore */
1175:   private static $_vars = array();
1176: 
1177:   /**
1178:    * @ignore
1179:    * 
1180:    * return wordpress permalink structure option
1181:    *
1182:    * @return string
1183:    */
1184:   public static function get_permalink_structure() {
1185:     if( ! isset( self::$_permalink_structure ) ) self::$_permalink_structure = get_option( "permalink_structure" );
1186:     
1187:     return self::$_permalink_structure;
1188:   }
1189:   
1190:   /**
1191:    * return home_url link in according to language slug.
1192:    *
1193:    * This function format home_url in according to $slug parameter
1194:    *
1195:    * @param string $slug - language slug. If null current slug will be used
1196:    *
1197:    * @return string
1198:    */
1199:   public static function get_home_url( $slug = null ) {
1200:     $_cml_settings = & $GLOBALS[ '_cml_settings' ];
1201: 
1202:     if( null === $slug ) $slug = CMLLanguage::get_default()->cml_language_slug;
1203: 
1204:     switch( CMLUtils::get_url_mode() ) {
1205:     case PRE_PATH:
1206:       if( $slug == CMLLanguage::get_default()->cml_language_slug &&
1207:           $_cml_settings[ 'url_mode_remove_default' ] ) {
1208:         $slug = "";
1209:       } else
1210:         $slug = "/$slug";
1211: 
1212:       $link = CMLUtils::home_url() . $slug;
1213:       break;
1214:     case PRE_DOMAIN:
1215:       $link = CMLUtils::home_url();
1216:       break;
1217:     default:
1218:       $link = CMLUtils::home_url() . "?lang=$slug";
1219:       break;
1220:     } //endswitch;
1221: 
1222:     return $link;
1223:   }
1224: 
1225:   /**
1226:    * @ignore
1227:    */
1228:   public static function get_url_mode() {
1229:     if( empty( self::$_url_mode ) )  {
1230:       global $_cml_settings;
1231:     
1232:       self::$_url_mode = $_cml_settings[ 'url_mode' ];  //more easy
1233:       
1234:       $permalink = self::get_permalink_structure();
1235:       if( empty( $permalink ) && self::$_url_mode == PRE_PATH )
1236:         self::$_url_mode = PRE_LANG;
1237:     }
1238: 
1239:     return self::$_url_mode;
1240:   }
1241:   
1242:   /**
1243:    * get wordpress date format option
1244:    * @ignore
1245:    */
1246:   public static function get_date_format() {
1247:     if( empty( self::$_date_format ) ) self::$_date_format = get_option( 'date_format' );
1248:     
1249:     return self::$_date_format;
1250:   }
1251:   
1252:   /**
1253:    * get "clean" home_url.
1254:    *
1255:    * The plugin translate home url in according to current language.
1256:    * So if you call the wp function "home_url" it will home url with language information,
1257:    * in according to url modification mode.
1258:    *
1259:    * This function will return "real" home without any language information.
1260:    *
1261:    * @example
1262:    *
1263:    * home_url()<br />
1264:    *  www.example.com/it<br />
1265:    * <br />
1266:    * CMLUtils::home_url()<br />
1267:    *  www.example.com<br />
1268:    * 
1269:    * @return string
1270:    */
1271:   public static function home_url() {
1272:     if( empty( self::$_home_url ) ) {
1273:       $GLOBALS[ '_cml_no_translate_home_url' ] = true;
1274:       self::$_home_url = home_url();
1275: 
1276:       unset( $GLOBALS[ '_cml_no_translate_home_url' ] );
1277:     }
1278: 
1279:     return self::$_home_url;
1280:   }
1281:   
1282:   /**
1283:    * remove language information from url
1284:    * 
1285:    * @ignore
1286:    */
1287:   public static function clear_url( $url = null ) {
1288:     global $wp_rewrite;
1289: 
1290:     if( self::get_url_mode() != PRE_PATH 
1291:         || ( self::$_clean_applied == true &&
1292:         null === $url ) ) {
1293:       return self::$_language_detected;
1294:     }
1295: 
1296:     if( empty( self::$_url ) ) {
1297:       self::$_url = "http://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
1298:       self::$_request_url = str_replace( trailingslashit( self::home_url() ),
1299:                                         "", self::$_url );
1300:     }
1301: 
1302:     if( null === $url ) {
1303:       $_url = self::$_url;
1304:       $request_url = self::$_request_url;
1305:       
1306:       /*
1307:        * remove all parameters in url
1308:        */
1309:       self::$_clean_url = preg_replace( "/\?.*/", "", $_url );
1310:       self::$_clean_request = $request_url;
1311:     } else {
1312:       $_url = $url;
1313:       $request_url = str_replace( trailingslashit( self::home_url() ),
1314:                                         "", $url );
1315:     }
1316: 
1317:     $_url = $request_url;
1318:     $base_url = str_replace( "http://" . $_SERVER['HTTP_HOST'], "", get_option( 'home' ) );
1319: 
1320:     if( preg_match( "#^([a-z]{2})(/.*)?$#i", $_url, $match ) ) {
1321:       $lang = CMLLanguage::get_id_by_slug( $match[1] );
1322:       if( empty( $lang ) ) {
1323:         return $url;
1324:       }
1325: 
1326:       $_url = substr( $_url, 3 );
1327:       $_url = preg_replace( "/\?.*/", "", $_url );
1328: 
1329:       if( null === $url ) {
1330:         self::$_language_detected = $lang;
1331: 
1332:         CMLLanguage::set_current( self::$_language_detected );
1333: 
1334:         self::$_clean_url = trailingslashit( self::$_home_url ) . $_url;
1335:         self::$_clean_request = trailingslashit( $base_url ) . $_url;
1336:       } else {
1337:         $_url = trailingslashit( CMLUtils::home_url() ) . $_url;
1338:       }
1339:     }
1340:     
1341:     if( null === $url ) {
1342:       self::$_clean_applied = true;
1343:     }
1344: 
1345:     return ( null === $url ) ? self::$_language_detected : $_url;
1346:   }
1347: 
1348:   /**
1349:    * return current url withouth any language information.
1350:    *
1351:    * @example
1352:    * 
1353:    *  www.example.com/en/example<br />
1354:    *<br />
1355:    *  CMLUtils::get_clean_url() will return:<br />
1356:    *<br />
1357:    *    www.example.com/example
1358:    *
1359:    * @return string
1360:    */
1361:   public static function get_clean_url() {
1362:     return self::$_clean_url;
1363:   }
1364:   
1365:   /**
1366:    * get $_SERVER[ 'REQUEST_URI' ] with no language information.
1367:    *
1368:    * @return string
1369:    */
1370:   public static function get_clean_request() {
1371:     return self::$_clean_request;
1372:   }
1373:   
1374:   /**
1375:    * @ignore
1376:    */
1377:   public static function _set( $key, $value ) {
1378:     self::$_vars[ $key ] = $value;
1379:   }
1380:   
1381:   /**
1382:    * @ignore
1383:    */
1384:   public static function _get( $key ) {
1385:     return isset( self::$_vars[ $key ] ) ? self::$_vars[ $key ] : null;
1386:   }
1387:   
1388:   /**
1389:    *@ignore
1390:    */
1391:   public static function _del( $key ) {
1392:     unset( self::$_vars[ $key ] );
1393:   }
1394: 
1395:   /**
1396:    * @ignore
1397:    */
1398:   public static function _append( $key, $value ) {
1399:     if( ! isset( self::$_vars[ $key ] ) ) {
1400:       self::$_vars[ $key ] = array();
1401:     }
1402:     
1403:     self::$_vars[ $key ][] = $value;
1404:   }
1405: }
1406: ?>
API documentation generated by ApiGen 2.8.0