// WordPress DB Class // ORIGINAL CODE FROM: // Justin Vincent (justin@visunet.ie) // http://php.justinvincent.com define('EZSQL_VERSION', 'WP1.25'); define('OBJECT', 'OBJECT', true); define('ARRAY_A', 'ARRAY_A', false); define('ARRAY_N', 'ARRAY_N', false); if (!defined('SAVEQUERIES')) define('SAVEQUERIES', false); class wpdb { var $show_errors = true; var $num_queries = 0; var $last_query; var $col_info; var $queries; // Our tables var $posts; var $users; var $categories; var $post2cat; var $comments; var $links; var $options; var $optiontypes; var $optionvalues; var $optiongroups; var $optiongroup_options; var $postmeta; var $usermeta; var $terms; var $term_taxonomy; var $term_relationships; var $charset; var $collate; /** * Connects to the database server and selects a database * @param string $dbuser * @param string $dbpassword * @param string $dbname * @param string $dbhost */ function wpdb($dbuser, $dbpassword, $dbname, $dbhost) { return $this->__construct($dbuser, $dbpassword, $dbname, $dbhost); } function __construct($dbuser, $dbpassword, $dbname, $dbhost) { register_shutdown_function(array(&$this, "__destruct")); if ( defined('DB_CHARSET') ) $this->charset = DB_CHARSET; if ( defined('DB_COLLATE') ) $this->collate = DB_COLLATE; $this->dbh = @mysql_connect($dbhost, $dbuser, $dbpassword); if (!$this->dbh) { $this->bail("

Error establishing a database connection

This either means that the username and password information in your wp-config.php file is incorrect or we can't contact the database server at $dbhost. This could mean your host's database server is down.

If you're unsure what these terms mean you should probably contact your host. If you still need help you can always visit the WordPress Support Forums.

"); } if ( !empty($this->charset) && version_compare(mysql_get_server_info(), '4.1.0', '>=') ) $this->query("SET NAMES '$this->charset'"); $this->select($dbname); } function __destruct() { return true; } /** * Selects a database using the current class's $this->dbh * @param string $db name */ function select($db) { if (!@mysql_select_db($db, $this->dbh)) { $this->bail("

Can’t select database

We were able to connect to the database server (which means your username and password is okay) but not able to select the $db database.

If you don't know how to setup a database you should contact your host. If all else fails you may find help at the WordPress Support Forums.

"); } } /** * Escapes content for insertion into the database, for security * * @param string $string * @return string query safe string */ function escape($string) { return addslashes( $string ); // Disable rest for now, causing problems if( !$this->dbh || version_compare( phpversion(), '4.3.0' ) == '-1' ) return mysql_escape_string( $string ); else return mysql_real_escape_string( $string, $this->dbh ); } /** * Escapes content by reference for insertion into the database, for security * @param string $s */ function escape_by_ref(&$s) { $s = $this->escape($s); } /** * Prepares a SQL query for safe use, using sprintf() syntax */ function prepare($args=NULL) { if ( NULL === $args ) return; $args = func_get_args(); $query = array_shift($args); $query = str_replace("'%s'", '%s', $query); // in case someone mistakenly already singlequoted it $query = str_replace('"%s"', '%s', $query); // doublequote unquoting $query = str_replace('%s', "'%s'", $query); // quote the strings array_walk($args, array(&$this, 'escape_by_ref')); return @vsprintf($query, $args); } // ================================================================== // Print SQL/DB error. function print_error($str = '') { global $EZSQL_ERROR; if (!$str) $str = mysql_error($this->dbh); $EZSQL_ERROR[] = array ('query' => $this->last_query, 'error_str' => $str); $str = htmlspecialchars($str, ENT_QUOTES); $query = htmlspecialchars($this->last_query, ENT_QUOTES); // Is error output turned on or not.. if ( $this->show_errors ) { // If there is an error then take note of it print "

WordPress database error: [$str]
$query

"; } else { return false; } } // ================================================================== // Turn error handling on or off.. function show_errors() { $this->show_errors = true; } function hide_errors() { $this->show_errors = false; } // ================================================================== // Kill cached query results function flush() { $this->last_result = array(); $this->col_info = null; $this->last_query = null; } // ================================================================== // Basic Query - see docs for more detail function query($query) { // filter the query, if filters are available // NOTE: some queries are made before the plugins have been loaded, and thus cannot be filtered with this method if ( function_exists('apply_filters') ) $query = apply_filters('query', $query); // initialise return $return_val = 0; $this->flush(); // Log how the function was called $this->func_call = "\$db->query(\"$query\")"; // Keep track of the last query for debug.. $this->last_query = $query; // Perform the query via std mysql_query function.. if (SAVEQUERIES) $this->timer_start(); $this->result = @mysql_query($query, $this->dbh); ++$this->num_queries; if (SAVEQUERIES) $this->queries[] = array( $query, $this->timer_stop() ); // If there is an error then take note of it.. if ( mysql_error($this->dbh) ) { $this->print_error(); return false; } if ( preg_match("/^\\s*(insert|delete|update|replace) /i",$query) ) { $this->rows_affected = mysql_affected_rows($this->dbh); // Take note of the insert_id if ( preg_match("/^\\s*(insert|replace) /i",$query) ) { $this->insert_id = mysql_insert_id($this->dbh); } // Return number of rows affected $return_val = $this->rows_affected; } else { $i = 0; while ($i < @mysql_num_fields($this->result)) { $this->col_info[$i] = @mysql_fetch_field($this->result); $i++; } $num_rows = 0; while ( $row = @mysql_fetch_object($this->result) ) { $this->last_result[$num_rows] = $row; $num_rows++; } @mysql_free_result($this->result); // Log number of rows the query returned $this->num_rows = $num_rows; // Return number of rows selected $return_val = $this->num_rows; } return $return_val; } /** * Get one variable from the database * @param string $query (can be null as well, for caching, see codex) * @param int $x = 0 row num to return * @param int $y = 0 col num to return * @return mixed results */ function get_var($query=null, $x = 0, $y = 0) { $this->func_call = "\$db->get_var(\"$query\",$x,$y)"; if ( $query ) $this->query($query); // Extract var out of cached results based x,y vals if ( $this->last_result[$y] ) { $values = array_values(get_object_vars($this->last_result[$y])); } // If there is a value return it else return null return (isset($values[$x]) && $values[$x]!=='') ? $values[$x] : null; } /** * Get one row from the database * @param string $query * @param string $output ARRAY_A | ARRAY_N | OBJECT * @param int $y row num to return * @return mixed results */ function get_row($query = null, $output = OBJECT, $y = 0) { $this->func_call = "\$db->get_row(\"$query\",$output,$y)"; if ( $query ) $this->query($query); else return null; if ( !isset($this->last_result[$y]) ) return null; if ( $output == OBJECT ) { return $this->last_result[$y] ? $this->last_result[$y] : null; } elseif ( $output == ARRAY_A ) { return $this->last_result[$y] ? get_object_vars($this->last_result[$y]) : null; } elseif ( $output == ARRAY_N ) { return $this->last_result[$y] ? array_values(get_object_vars($this->last_result[$y])) : null; } else { $this->print_error(" \$db->get_row(string query, output type, int offset) -- Output type must be one of: OBJECT, ARRAY_A, ARRAY_N"); } } /** * Gets one column from the database * @param string $query (can be null as well, for caching, see codex) * @param int $x col num to return * @return array results */ function get_col($query = null , $x = 0) { if ( $query ) $this->query($query); $new_array = array(); // Extract the column values for ( $i=0; $i < count($this->last_result); $i++ ) { $new_array[$i] = $this->get_var(null, $x, $i); } return $new_array; } /** * Return an entire result set from the database * @param string $query (can also be null to pull from the cache) * @param string $output ARRAY_A | ARRAY_N | OBJECT * @return mixed results */ function get_results($query = null, $output = OBJECT) { $this->func_call = "\$db->get_results(\"$query\", $output)"; if ( $query ) $this->query($query); else return null; // Send back array of objects. Each row is an object if ( $output == OBJECT ) { return $this->last_result; } elseif ( $output == ARRAY_A || $output == ARRAY_N ) { if ( $this->last_result ) { $i = 0; foreach( $this->last_result as $row ) { $new_array[$i] = (array) $row; if ( $output == ARRAY_N ) { $new_array[$i] = array_values($new_array[$i]); } $i++; } return $new_array; } else { return null; } } } /** * Grabs column metadata from the last query * @param string $info_type one of name, table, def, max_length, not_null, primary_key, multiple_key, unique_key, numeric, blob, type, unsigned, zerofill * @param int $col_offset 0: col name. 1: which table the col's in. 2: col's max length. 3: if the col is numeric. 4: col's type * @return mixed results */ function get_col_info($info_type = 'name', $col_offset = -1) { if ( $this->col_info ) { if ( $col_offset == -1 ) { $i = 0; foreach($this->col_info as $col ) { $new_array[$i] = $col->{$info_type}; $i++; } return $new_array; } else { return $this->col_info[$col_offset]->{$info_type}; } } } /** * Starts the timer, for debugging purposes */ function timer_start() { $mtime = microtime(); $mtime = explode(' ', $mtime); $this->time_start = $mtime[1] + $mtime[0]; return true; } /** * Stops the debugging timer * @return int total time spent on the query, in milliseconds */ function timer_stop() { $mtime = microtime(); $mtime = explode(' ', $mtime); $time_end = $mtime[1] + $mtime[0]; $time_total = $time_end - $this->time_start; return $time_total; } /** * Wraps fatal errors in a nice header and footer and dies. * @param string $message */ function bail($message) { // Just wraps errors in a nice header and footer if ( !$this->show_errors ) return false; wp_die($message); } } if ( ! isset($wpdb) ) $wpdb = new wpdb(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST); ?> // Strip, trim, kses, special chars for string saves $filters = array('pre_term_name', 'pre_comment_author_name', 'pre_link_name', 'pre_link_target', 'pre_link_rel', 'pre_user_display_name', 'pre_user_first_name', 'pre_user_last_name', 'pre_user_nickname'); foreach ( $filters as $filter ) { add_filter($filter, 'strip_tags'); add_filter($filter, 'trim'); add_filter($filter, 'wp_filter_kses'); add_filter($filter, 'wp_specialchars', 30); } // Kses only for textarea saves $filters = array('pre_term_description', 'pre_link_description', 'pre_link_notes', 'pre_user_description'); foreach ( $filters as $filter ) { add_filter($filter, 'wp_filter_kses'); } // Email $filters = array('pre_comment_author_email', 'pre_user_email'); foreach ( $filters as $filter ) { add_filter($filter, 'trim'); add_filter($filter, 'sanitize_email'); add_filter($filter, 'wp_filter_kses'); } // URL $filters = array('pre_comment_author_url', 'pre_user_url', 'pre_link_url', 'pre_link_image', 'pre_link_rss', 'comment_url'); foreach ( $filters as $filter ) { add_filter($filter, 'strip_tags'); add_filter($filter, 'trim'); add_filter($filter, 'clean_url'); add_filter($filter, 'wp_filter_kses'); } // Slugs $filters = array('pre_term_slug'); foreach ( $filters as $filter ) { add_filter($filter, 'sanitize_title'); } // Places to balance tags on input $filters = array('content_save_pre', 'excerpt_save_pre', 'comment_save_pre', 'pre_comment_content'); foreach ( $filters as $filter ) { add_filter( $filter, 'balanceTags', 50); } // Format strings for display. $filters = array('comment_author', 'term_name', 'link_name', 'link_description', 'link_notes', 'bloginfo', 'wp_title'); foreach ( $filters as $filter ) { add_filter($filter, 'wptexturize'); add_filter($filter, 'convert_chars'); add_filter($filter, 'wp_specialchars'); } // Format text area for display. $filters = array('term_description'); foreach ( $filters as $filter ) { add_filter($filter, 'wptexturize'); add_filter($filter, 'convert_chars'); add_filter($filter, 'wpautop'); } // Format for RSS $filters = array('term_name_rss'); foreach ( $filters as $filter ) { add_filter($filter, 'convert_chars'); } // Display filters add_filter('the_title', 'wptexturize'); add_filter('the_title', 'convert_chars'); add_filter('the_title', 'trim'); add_filter('the_content', 'wptexturize'); add_filter('the_content', 'convert_smilies'); add_filter('the_content', 'convert_chars'); add_filter('the_content', 'wpautop'); add_filter('the_excerpt', 'wptexturize'); add_filter('the_excerpt', 'convert_smilies'); add_filter('the_excerpt', 'convert_chars'); add_filter('the_excerpt', 'wpautop'); add_filter('get_the_excerpt', 'wp_trim_excerpt'); add_filter('comment_text', 'wptexturize'); add_filter('comment_text', 'convert_chars'); add_filter('comment_text', 'make_clickable', 9); add_filter('comment_text', 'force_balance_tags', 25); add_filter('comment_text', 'convert_smilies', 20); add_filter('comment_text', 'wpautop', 30); add_filter('comment_excerpt', 'convert_chars'); add_filter('list_cats', 'wptexturize'); add_filter('single_post_title', 'wptexturize'); // RSS filters add_filter('the_title_rss', 'strip_tags'); add_filter('the_title_rss', 'ent2ncr', 8); add_filter('the_title_rss', 'wp_specialchars'); add_filter('the_content_rss', 'ent2ncr', 8); add_filter('the_excerpt_rss', 'convert_chars'); add_filter('the_excerpt_rss', 'ent2ncr', 8); add_filter('comment_author_rss', 'ent2ncr', 8); add_filter('comment_text_rss', 'ent2ncr', 8); add_filter('comment_text_rss', 'wp_specialchars'); add_filter('bloginfo_rss', 'ent2ncr', 8); add_filter('the_author', 'ent2ncr', 8); // Misc filters add_filter('option_ping_sites', 'privacy_ping_filter'); add_filter('option_blog_charset', 'wp_specialchars'); add_filter('option_home', '_config_wp_home'); add_filter('option_siteurl', '_config_wp_siteurl'); add_filter('mce_plugins', '_mce_load_rtl_plugin'); add_filter('mce_buttons', '_mce_add_direction_buttons'); add_filter('pre_kses', 'wp_pre_kses_less_than'); add_filter('sanitize_title', 'sanitize_title_with_dashes'); add_action('check_comment_flood', 'check_comment_flood_db', 10, 3); add_filter('comment_flood_filter', 'wp_throttle_comment_flood', 10, 3); add_filter('pre_comment_content', 'wp_rel_nofollow', 15); add_filter('comment_email', 'antispambot'); // Actions add_action('wp_head', 'rsd_link'); add_action('wp_head', 'locale_stylesheet'); add_action('publish_future_post', 'wp_publish_post', 10, 1); add_action('wp_head', 'noindex', 1); add_action('wp_head', 'wp_print_scripts'); if(!defined('DOING_CRON')) add_action('init', 'wp_cron'); add_action('do_feed_rdf', 'do_feed_rdf', 10, 1); add_action('do_feed_rss', 'do_feed_rss', 10, 1); add_action('do_feed_rss2', 'do_feed_rss2', 10, 1); add_action('do_feed_atom', 'do_feed_atom', 10, 1); add_action('do_pings', 'do_all_pings', 10, 1); add_action('do_robots', 'do_robots'); add_action('sanitize_comment_cookies', 'sanitize_comment_cookies'); add_action('admin_print_scripts', 'wp_print_scripts', 20); add_action('mce_options', '_mce_set_direction'); add_action('init', 'smilies_init', 5); add_action( 'plugins_loaded', 'wp_maybe_load_widgets', 0 ); add_action( 'shutdown', 'wp_ob_end_flush_all', 1); add_action('publish_post', '_publish_post_hook', 5, 1); add_action('future_post', '_future_post_hook', 5, 2); add_action('future_page', '_future_post_hook', 5, 2); add_action('save_post', '_save_post_hook', 5, 2); add_action('transition_post_status', '_transition_post_status', 5, 3); add_action('comment_form', 'wp_comment_form_unfiltered_html_nonce'); // Redirect Old Slugs add_action('template_redirect', 'wp_old_slug_redirect'); add_action('edit_post', 'wp_check_for_changed_slugs'); add_action('edit_form_advanced', 'wp_remember_old_slug'); ?> /* Copyright (c) 2003 Danilo Segan . Copyright (c) 2005 Nico Kaiser This file is part of PHP-gettext. PHP-gettext is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. PHP-gettext is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with PHP-gettext; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * Provides a simple gettext replacement that works independently from * the system's gettext abilities. * It can read MO files and use them for translating strings. * The files are passed to gettext_reader as a Stream (see streams.php) * * This version has the ability to cache all strings and translations to * speed up the string lookup. * While the cache is enabled by default, it can be switched off with the * second parameter in the constructor (e.g. whenusing very large MO files * that you don't want to keep in memory) */ class gettext_reader { //public: var $error = 0; // public variable that holds error code (0 if no error) //private: var $BYTEORDER = 0; // 0: low endian, 1: big endian var $STREAM = NULL; var $short_circuit = false; var $enable_cache = false; var $originals = NULL; // offset of original table var $translations = NULL; // offset of translation table var $pluralheader = NULL; // cache header field for plural forms var $select_string_function = NULL; // cache function, which chooses plural forms var $total = 0; // total string count var $table_originals = NULL; // table for original strings (offsets) var $table_translations = NULL; // table for translated strings (offsets) var $cache_translations = NULL; // original -> translation mapping /* Methods */ /** * Reads a 32bit Integer from the Stream * * @access private * @return Integer from the Stream */ function readint() { if ($this->BYTEORDER == 0) { // low endian $low_end = unpack('V', $this->STREAM->read(4)); return array_shift($low_end); } else { // big endian $big_end = unpack('N', $this->STREAM->read(4)); return array_shift($big_end); } } /** * Reads an array of Integers from the Stream * * @param int count How many elements should be read * @return Array of Integers */ function readintarray($count) { if ($this->BYTEORDER == 0) { // low endian return unpack('V'.$count, $this->STREAM->read(4 * $count)); } else { // big endian return unpack('N'.$count, $this->STREAM->read(4 * $count)); } } /** * Constructor * * @param object Reader the StreamReader object * @param boolean enable_cache Enable or disable caching of strings (default on) */ function gettext_reader($Reader, $enable_cache = true) { // If there isn't a StreamReader, turn on short circuit mode. if (! $Reader || isset($Reader->error) ) { $this->short_circuit = true; return; } // Caching can be turned off $this->enable_cache = $enable_cache; // $MAGIC1 = (int)0x950412de; //bug in PHP 5.0.2, see https://savannah.nongnu.org/bugs/?func=detailitem&item_id=10565 $MAGIC1 = (int) - 1794895138; // $MAGIC2 = (int)0xde120495; //bug $MAGIC2 = (int) - 569244523; // 64-bit fix $MAGIC3 = (int) 2500072158; $this->STREAM = $Reader; $magic = $this->readint(); if ($magic == ($MAGIC1 & 0xFFFFFFFF) || $magic == ($MAGIC3 & 0xFFFFFFFF)) { // to make sure it works for 64-bit platforms $this->BYTEORDER = 0; } elseif ($magic == ($MAGIC2 & 0xFFFFFFFF)) { $this->BYTEORDER = 1; } else { $this->error = 1; // not MO file return false; } // FIXME: Do we care about revision? We should. $revision = $this->readint(); $this->total = $this->readint(); $this->originals = $this->readint(); $this->translations = $this->readint(); } /** * Loads the translation tables from the MO file into the cache * If caching is enabled, also loads all strings into a cache * to speed up translation lookups * * @access private */ function load_tables() { if (is_array($this->cache_translations) && is_array($this->table_originals) && is_array($this->table_translations)) return; /* get original and translations tables */ $this->STREAM->seekto($this->originals); $this->table_originals = $this->readintarray($this->total * 2); $this->STREAM->seekto($this->translations); $this->table_translations = $this->readintarray($this->total * 2); if ($this->enable_cache) { $this->cache_translations = array (); /* read all strings in the cache */ for ($i = 0; $i < $this->total; $i++) { $this->STREAM->seekto($this->table_originals[$i * 2 + 2]); $original = $this->STREAM->read($this->table_originals[$i * 2 + 1]); $this->STREAM->seekto($this->table_translations[$i * 2 + 2]); $translation = $this->STREAM->read($this->table_translations[$i * 2 + 1]); $this->cache_translations[$original] = $translation; } } } /** * Returns a string from the "originals" table * * @access private * @param int num Offset number of original string * @return string Requested string if found, otherwise '' */ function get_original_string($num) { $length = $this->table_originals[$num * 2 + 1]; $offset = $this->table_originals[$num * 2 + 2]; if (! $length) return ''; $this->STREAM->seekto($offset); $data = $this->STREAM->read($length); return (string)$data; } /** * Returns a string from the "translations" table * * @access private * @param int num Offset number of original string * @return string Requested string if found, otherwise '' */ function get_translation_string($num) { $length = $this->table_translations[$num * 2 + 1]; $offset = $this->table_translations[$num * 2 + 2]; if (! $length) return ''; $this->STREAM->seekto($offset); $data = $this->STREAM->read($length); return (string)$data; } /** * Binary search for string * * @access private * @param string string * @param int start (internally used in recursive function) * @param int end (internally used in recursive function) * @return int string number (offset in originals table) */ function find_string($string, $start = -1, $end = -1) { if (($start == -1) or ($end == -1)) { // find_string is called with only one parameter, set start end end $start = 0; $end = $this->total; } if (abs($start - $end) <= 1) { // We're done, now we either found the string, or it doesn't exist $txt = $this->get_original_string($start); if ($string == $txt) return $start; else return -1; } else if ($start > $end) { // start > end -> turn around and start over return $this->find_string($string, $end, $start); } else { // Divide table in two parts $half = (int)(($start + $end) / 2); $cmp = strcmp($string, $this->get_original_string($half)); if ($cmp == 0) // string is exactly in the middle => return it return $half; else if ($cmp < 0) // The string is in the upper half return $this->find_string($string, $start, $half); else // The string is in the lower half return $this->find_string($string, $half, $end); } } /** * Translates a string * * @access public * @param string string to be translated * @return string translated string (or original, if not found) */ function translate($string) { if ($this->short_circuit) return $string; $this->load_tables(); if ($this->enable_cache) { // Caching enabled, get translated string from cache if (array_key_exists($string, $this->cache_translations)) return $this->cache_translations[$string]; else return $string; } else { // Caching not enabled, try to find string $num = $this->find_string($string); if ($num == -1) return $string; else return $this->get_translation_string($num); } } /** * Get possible plural forms from MO header * * @access private * @return string plural form header */ function get_plural_forms() { // lets assume message number 0 is header // this is true, right? $this->load_tables(); // cache header field for plural forms if (! is_string($this->pluralheader)) { if ($this->enable_cache) { $header = $this->cache_translations[""]; } else { $header = $this->get_translation_string(0); } $header .= "\n"; //make sure our regex matches if (eregi("plural-forms: ([^\n]*)\n", $header, $regs)) $expr = $regs[1]; else $expr = "nplurals=2; plural=n == 1 ? 0 : 1;"; // add parentheses // important since PHP's ternary evaluates from left to right $expr.= ';'; $res= ''; $p= 0; for ($i= 0; $i < strlen($expr); $i++) { $ch= $expr[$i]; switch ($ch) { case '?': $res.= ' ? ('; $p++; break; case ':': $res.= ') : ('; break; case ';': $res.= str_repeat( ')', $p) . ';'; $p= 0; break; default: $res.= $ch; } } $this->pluralheader = $res; } return $this->pluralheader; } /** * Detects which plural form to take * * @access private * @param n count * @return int array index of the right plural form */ function select_string($n) { if (is_null($this->select_string_function)) { $string = $this->get_plural_forms(); if (preg_match("/nplurals\s*=\s*(\d+)\s*\;\s*plural\s*=\s*(.*?)\;+/", $string, $matches)) { $nplurals = $matches[1]; $expression = $matches[2]; $expression = str_replace("n", '$n', $expression); } else { $nplurals = 2; $expression = ' $n == 1 ? 0 : 1 '; } $func_body = " \$plural = ($expression); return (\$plural <= $nplurals)? \$plural : \$plural - 1;"; $this->select_string_function = create_function('$n', $func_body); } return call_user_func($this->select_string_function, $n); } /** * Plural version of gettext * * @access public * @param string single * @param string plural * @param string number * @return translated plural form */ function ngettext($single, $plural, $number) { if ($this->short_circuit) { if ($number != 1) return $plural; else return $single; } // find out the appropriate form $select = $this->select_string($number); // this should contains all strings separated by NULLs $key = $single.chr(0).$plural; if ($this->enable_cache) { if (! array_key_exists($key, $this->cache_translations)) { return ($number != 1) ? $plural : $single; } else { $result = $this->cache_translations[$key]; $list = explode(chr(0), $result); return $list[$select]; } } else { $num = $this->find_string($key); if ($num == -1) { return ($number != 1) ? $plural : $single; } else { $result = $this->get_translation_string($num); $list = explode(chr(0), $result); return $list[$select]; } } } } ?>