def wp_load_core_site_options(site_id=None): ''' Loads and caches certain often requested site options if WpC.WB.Wj.is_multisite() and a persistent cache is not being used. @global wpdb wpdb WordPress database abstraction object. @param int site_id Optional site ID for which to query the options. Defaults to the current site. ''' wpdb = WpC.WB.Wj.wpdb # global wpdb if WpC.WB.Wj.is_multisite() or wp_using_ext_object_cache( ) or wp_installing(): return if Php.empty(locals(), 'site_id'): site_id = wpdb.siteid core_options = array('site_name', 'siteurl', 'active_sitewide_plugins', '_site_transient_timeout_theme_roots', '_site_transient_theme_roots', 'site_admins', 'can_compress_scripts', 'global_terms_enabled', 'ms_files_rewriting') core_options_in = "'" + Php.implode("', '", core_options) + "'" options = wpdb.get_results( wpdb.prepare( "SELECT meta_key, meta_value FROM {} WHERE meta_key IN ({}) AND site_id = %d" .format(wpdb.sitemeta, core_options_in), site_id)) for option in options: key = option.meta_key cache_key = site_id + ":" + key option.meta_value = maybe_unserialize(option.meta_value) WiCa.wp_cache_set(cache_key, option.meta_value, 'site-options')
def parse_orderby(self, orderby_raw): ''' Parse and sanitize 'orderby' keys passed to the term query. @global wpdb wpdb WordPress database abstraction object. @param string orderby_raw Alias for the field to order by. @return string|False Value to used in the ORDER clause. False otherwise. ''' _orderby = Php.strtolower(orderby_raw) maybe_orderby_meta = False if Php.in_array(_orderby, array('term_id', 'name', 'slug', 'term_group'), True): orderby = "t.$_orderby" elif Php.in_array( _orderby, array('count', 'parent', 'taxonomy', 'term_taxonomy_id', 'description'), True): orderby = "tt." + _orderby elif 'term_order' == _orderby: orderby = 'tr.term_order' if 'count' == _orderby: orderby = 'tt.count' elif 'name' == _orderby: orderby = 't.name' elif 'slug' == _orderby: orderby = 't.slug' elif 'include' == _orderby and not Php.empty(self.query_vars, 'include'): include = Php.implode( ',', WiFc.wp_parse_id_list(self.query_vars['include'])) orderby = "FIELD( t.term_id, {} )".format(include) elif 'none' == _orderby: orderby = '' # elif Php.empty(locals(), '_orderby') or ... elif not _orderby or 'id' == _orderby or 'term_id' == _orderby: orderby = 't.term_id' else: orderby = 't.name' # This may be a value of orderby related to meta. maybe_orderby_meta = True # Filters the ORDERBY clause of the terms query. # @param string orderby `ORDERBY` clause of the terms query. # @param array args An array of terms query arguments. # @param array taxonomies An array of taxonomies. orderby = WiPg.apply_filters('get_terms_orderby', orderby, self.query_vars, self.query_vars['taxonomy']) # Run after the 'get_terms_orderby' filter for backward compatibility. if maybe_orderby_meta: maybe_orderby_meta = self.parse_orderby_meta(_orderby) if maybe_orderby_meta: orderby = maybe_orderby_meta return orderby
def get_terms(self): ''' Get terms, based on query_vars. @global wpdb wpdb WordPress database abstraction object. @return array ''' import wp.i.taxonomy as WiTx wpdb = WpC.WB.Wj.wpdb # global wpdb self.parse_query(self.query_vars) args = self.query_vars #pprint(self.query_vars) # TypeError: unhashable type: 'instancemethod' # userdata is array, inspect.ismethod(A.__repr__) is True print('WP_Term_Query.get_terms: self.query_vars=', self.query_vars) # Set up meta_query so it's available to 'pre_get_terms'. self.meta_query = WcMQ.WP_Meta_Query() self.meta_query.parse_query_vars(args) # Fires before terms are retrieved. # @param WP_Term_Query self Current instance of WP_Term_Query. WiPg.do_action('pre_get_terms', self) taxonomies = args['taxonomy'] print("WcTQ.get_terms: taxonomies=", taxonomies) # Save queries by not crawling the tree in the case of multiple taxes or a flat tax. has_hierarchical_tax = False if taxonomies: for _tax in taxonomies: if WiTx.is_taxonomy_hierarchical(_tax): has_hierarchical_tax = True if not has_hierarchical_tax: args['hierarchical'] = False args['pad_counts'] = False # 'parent' overrides 'child_of'. if 0 < Php.intval(args['parent']): args['child_of'] = False if 'all' == args['get']: args['childless'] = False args['child_of'] = 0 args['hide_empty'] = 0 args['hierarchical'] = False args['pad_counts'] = False # Filters the terms query arguments. # @param array args An array of get_terms() arguments. # @param array taxonomies An array of taxonomies. args = WiPg.apply_filters('get_terms_args', args, taxonomies) #pprint(args) # TypeError: unhashable type: 'instancemethod' # userdata is array, inspect.ismethod(A.__repr__) is True print('WP_Term_Query.get_terms: args=', args) # Avoid the query if the queried parent/child_of term has no descendants. child_of = args['child_of'] parent = args['parent'] if child_of: _parent = child_of elif parent: _parent = parent else: _parent = False if _parent: in_hierarchy = False for _tax in taxonomies: hierarchy = WiTx._get_term_hierarchy(_tax) if Php.isset(hierarchy, _parent): in_hierarchy = True if not in_hierarchy: return array() # 'term_order' is a legal sort order only when joining the relationship # table. _orderby = self.query_vars['orderby'] if 'term_order' == _orderby and Php.empty(self.query_vars, 'object_ids'): _orderby = 'term_id' orderby = self.parse_orderby(_orderby) if orderby: orderby = "ORDER BY " + orderby order = self.parse_order(self.query_vars['order']) if taxonomies: self.sql_clauses['where']['taxonomy'] = ( "tt.taxonomy IN ('" + Php.implode("', '", Php.array_map(WiF.esc_sql, taxonomies)) + "')") exclude = args['exclude'] exclude_tree = args['exclude_tree'] include = args['include'] inclusions = '' if include: # if not Php.empty(locals(), 'include'): exclude = '' exclude_tree = '' inclusions = Php.implode(',', WiFc.wp_parse_id_list(include)) if inclusions: # if not Php.empty(locals(), 'inclusions'): self.sql_clauses['where']['inclusions'] = ('t.term_id IN ( ' + inclusions + ' )') exclusions = array() # Php.array_map( int, exclusions=List) if exclude_tree: # if not Php.empty(locals(), 'exclude_tree'): exclude_tree = WiFc.wp_parse_id_list(exclude_tree) excluded_children = exclude_tree for extrunk in exclude_tree: excluded_children = Php.array_merge( excluded_children, Php.Array( get_terms( taxonomies[0], array( ('child_of', Php.intval(extrunk)), ('fields', 'ids'), ('hide_empty', 0), )))) exclusions = Php.array_merge(excluded_children, exclusions) if exclude: # if not Php.empty(locals(), 'exclude'): exclusions = Php.array_merge(WiFc.wp_parse_id_list(exclude), exclusions) # 'childless' terms are those without an entry in the flattened term hierarchy. childless = bool(args['childless']) if childless: for _tax in taxonomies: term_hierarchy = WiTx._get_term_hierarchy(_tax) exclusions = Php.array_merge(Php.array_keys(term_hierarchy), exclusions) if exclusions: # if not Php.empty(locals(), 'exclusions'): exclusions = 't.term_id NOT IN (' + Php.implode( ',', Php.array_map(Php.intval, exclusions)) + ')' else: exclusions = '' # Filters the terms to exclude from the terms query. # @param string exclusions `NOT IN` clause of the terms query. # @param array args An array of terms query arguments. # @param array taxonomies An array of taxonomies. exclusions = WiPg.apply_filters('list_terms_exclusions', exclusions, args, taxonomies) if exclusions: # if not Php.empty(locals(), 'exclusions'): # Must do string manipulation here for backward compatibility with filter. self.sql_clauses['where']['exclusions'] = preg_replace( '/^\s*AND\s*/', '', exclusions) print("\n WcTQ.get_terms: args['name'] =", args['name']) print("WcTQ.get_terms: taxonomies=", taxonomies) if not Php.empty(args, 'name'): names = Php.Array(args['name']) print("WcTQ.get_terms: names=", names, taxonomies) #foreach ( names as &_name ) { #modify list entries during for loop stackoverflow.com/questions/4081217 for k, _name in names.items( ): #use enumerate(names) if type(names)=list # `sanitize_term_field()` returns slashed data. #_name = Php.stripslashes( WiTx.sanitize_term_field( # 'name', _name, 0, Php.reset(taxonomies), 'db')) names[k] = Php.stripslashes( WiTx.sanitize_term_field('name', _name, 0, Php.reset(taxonomies), 'db')) print("WcTQ.get_terms: names=", names, taxonomies) self.sql_clauses['where']['name'] = "t.name IN ('" + Php.implode( "', '", Php.array_map(WiF.esc_sql, names)) + "')" if not Php.empty(args, 'slug'): if Php.is_array(args['slug']): slug = Php.array_map(WiF.sanitize_title, args['slug']) self.sql_clauses['where'][ 'slug'] = "t.slug IN ('" + Php.implode("', '", slug) + "')" else: slug = WiF.sanitize_title(args['slug']) self.sql_clauses['where']['slug'] = "t.slug = 'slug'" if not Php.empty(args, 'term_taxonomy_id'): if Php.is_array(args['term_taxonomy_id']): tt_ids = Php.implode( ',', Php.array_map(Php.intval, args['term_taxonomy_id'])) self.sql_clauses['where']['term_taxonomy_id'] = \ "tt.term_taxonomy_id IN ({})".format(tt_ids) else: self.sql_clauses['where']['term_taxonomy_id'] = wpdb.prepare( "tt.term_taxonomy_id = %s", args['term_taxonomy_id']) # PyMySQL %d->%s if not Php.empty(args, 'name__like'): self.sql_clauses['where']['name__like'] = wpdb.prepare( "t.name LIKE %s", '%' + wpdb.esc_like(args['name__like']) + '%') if not Php.empty(args, 'description__like'): self.sql_clauses['where']['description__like'] = wpdb.prepare( "tt.description LIKE %s", '%' + wpdb.esc_like(args['description__like']) + '%') if not Php.empty(args, 'object_ids'): object_ids = args['object_ids'] if not Php.is_array(object_ids): object_ids = array(object_ids) object_ids = Php.implode(', ', Php.array_map(Php.intval, object_ids)) self.sql_clauses['where'][ 'object_ids'] = "tr.object_id IN ({})".format(object_ids) # When querying for object relationships, the 'count > 0' check # added by 'hide_empty' is superfluous. if not Php.empty(args['object_ids']): args['hide_empty'] = False if '' != parent: parent = Php.intval(parent) self.sql_clauses['where']['parent'] = "tt.parent = 'parent'" hierarchical = args['hierarchical'] if 'count' == args['fields']: hierarchical = False if args['hide_empty'] and not hierarchical: self.sql_clauses['where']['count'] = 'tt.count > 0' number = args['number'] offset = args['offset'] # Don't limit the query results when we have to descend the family tree. if number and not hierarchical and not child_of and '' == parent: if offset: limits = 'LIMIT ' + offset + ',' + number else: limits = 'LIMIT ' + number else: limits = '' if not Php.empty(args, 'search'): self.sql_clauses['where']['search'] = self.get_search_sql( args['search']) # Meta query support. join = '' distinct = '' # Reparse meta_query query_vars, in case they were modified in a 'pre_get_terms' callback. self.meta_query.parse_query_vars(self.query_vars) mq_sql = self.meta_query.get_sql('term', 't', 'term_id') meta_clauses = self.meta_query.get_clauses() if not Php.empty(args, 'meta_clauses'): join += mq_sql['join'] self.sql_clauses['where']['meta_query'] = preg_replace( '/^\s*AND\s*/', '', mq_sql['where']) distinct += "DISTINCT" selects = array() #switch ( args['fields'] ) { # case 'all': AF = args['fields'] if AF in ('all', 'all_with_object_id', 'tt_ids', 'slugs'): selects = array('t.*', 'tt.*') if ('all_with_object_id' == args['fields'] and not Php.empty(args, 'object_ids')): selects[None] = 'tr.object_id' #elif AF in ('ids', 'id=>parent'): elif AF in ('ids', 'id=>parent', 'id.parent'): selects = array('t.term_id', 'tt.parent', 'tt.count', 'tt.taxonomy') elif AF == 'names': selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name', 'tt.taxonomy') elif AF == 'count': orderby = '' order = '' selects = array('COUNT(*)', ) #elif AF == 'id=>name': elif AF in ('id=>name', 'id.name'): selects = array('t.term_id', 't.name', 'tt.count', 'tt.taxonomy') #elif AF == 'id=>slug': elif AF in ('id=>slug', 'id.slug'): selects = array('t.term_id', 't.slug', 'tt.count', 'tt.taxonomy') _fields = args['fields'] # Filters the fields to select in the terms query. # Field lists modified using this filter will only modify the term fields returned # by the function when the `fields` parameter set to 'count' or 'all'. In all other # cases, the term fields in the results array will be determined by the `fields` # parameter alone. # Use of this filter can result in unpredictable behavior, and is not recommended. # @param array selects An array of fields to select for the terms query. # @param array args An array of term query arguments. # @param array taxonomies An array of taxonomies. fields = Php.implode( ', ', WiPg.apply_filters('get_terms_fields', selects, args, taxonomies)) join += (" INNER JOIN " + wpdb.term_taxonomy + " AS tt ON t.term_id = tt.term_id") if not Php.empty(self.query_vars, 'object_ids'): join += (" INNER JOIN {} AS tr ON tr.term_taxonomy_id = " "tt.term_taxonomy_id".format(wpdb.term_relationships)) where = Php.implode(' AND ', self.sql_clauses['where']) # Filters the terms query SQL clauses. # @param array pieces Terms query SQL clauses. # @param array taxonomies An array of taxonomies. # @param array args An array of terms query arguments. pieces = ('fields', 'join', 'where', 'distinct', 'orderby', 'order', 'limits') clauses = WiPg.apply_filters('terms_clauses', Php.compact(locals(), pieces), taxonomies, args) #fields = isset( clauses[ 'fields' ] ) ? clauses[ 'fields' ] : '' fields = clauses.get('fields', '') join = clauses.get('join', '') where = clauses.get('where', '') distinct = clauses.get('distinct', '') orderby = clauses.get('orderby', '') order = clauses.get('order', '') limits = clauses.get('limits', '') if where: where = "WHERE " + where self.sql_clauses['select'] = "SELECT {} {}".format(distinct, fields) self.sql_clauses['from'] = "FROM {} AS t {}".format(wpdb.terms, join) self.sql_clauses['orderby'] = orderby + " " + order if orderby else '' self.sql_clauses['limits'] = limits #self.request = "{self.sql_clauses['select']} {self.sql_clauses['from']} {where} {self.sql_clauses['orderby']} {self.sql_clauses['limits']}" self.request = "{} {} {} {} {}".format(self.sql_clauses['select'], self.sql_clauses['from'], where, self.sql_clauses['orderby'], self.sql_clauses['limits']) # args can be anything. Only use the args defined in defaults to compute the key. key = Php.md5( Php.serialize( WiFc.wp_array_slice_assoc( args, Php.array_keys(self.query_var_defaults))) + Php.serialize(taxonomies) + self.request) last_changed = WiFc.wp_cache_get_last_changed('terms') cache_key = "get_terms:{}:{}".format(key, last_changed) cache = WiCa.wp_cache_get(cache_key, 'terms') if False != cache: if 'all' == _fields: cache = Php.array_map(WiTx.get_term, cache) self.terms = cache print("WcTQ get_terms 1 self.terms=", self.terms) return self.terms if 'count' == _fields: count = wpdb.get_var(self.request) WiCa.wp_cache_set(cache_key, count, 'terms') return count terms = wpdb.get_results(self.request) print("WcTQ get_terms 2 terms=", terms) if 'all' == _fields or 'all_with_object_id' == _fields: WiTx.update_term_cache(terms) print("WcTQ get_terms 3 terms=", terms) # Prime termmeta cache. if args['update_term_meta_cache']: term_ids = WiFc.wp_list_pluck(terms, 'term_id') WiTx.update_termmeta_cache(term_ids) print("WcTQ get_terms 4 terms=", terms) if not terms: # if Php.empty(locals(), 'terms'): WiCa.wp_cache_add(cache_key, array(), 'terms', WpC.WB.Wj.DAY_IN_SECONDS) return array() print("WcTQ get_terms 5 terms=", terms) if child_of: for _tax in taxonomies: children = WiTx._get_term_hierarchy(_tax) if children: # if not Php.empty(locals(), 'children'): terms = _get_term_children(child_of, terms, _tax) print("WcTQ get_terms 6 terms=", terms) # Update term counts to include children. if args['pad_counts'] and 'all' == _fields: for _tax in taxonomies: _pad_term_counts(terms, _tax) # Make sure we show empty categories that have children. if hierarchical and args['hide_empty'] and Php.is_array(terms): for k, term in terms.items(): Continue2 = False #VT added to translate php: continue 2 if not term.count: children = get_term_children(term.term_id, term.taxonomy) if Php.is_array(children): for child_id in children: child = WiTx.get_term(child_id, term.taxonomy) if child.count: #continue 2 Continue2 = True #VT added to translate php: continue 2 continue #VT added to translate php: continue 2 # It really is empty. del terms[k] if Continue2: #VT added to translate php: continue 2 continue #VT added to translate php: continue 2 print("WcTQ get_terms 7 terms=", terms) # When querying for terms connected to objects, we may get # duplicate results. The duplicates should be preserved if # `fields` is 'all_with_object_id', but should otherwise be # removed. if not Php.empty(args, 'object_ids') and 'all_with_object_id' != _fields: _tt_ids = array() # need to be sperate mutable obj _terms = array() # need to be sperate mutable obj for term in terms: if Php.isset(_tt_ids, getattr(term, 'term_id', None)): continue _tt_ids[term.term_id] = 1 _terms[None] = term terms = _terms _terms = array() # array() #if 'id=>parent' == _fields: if _fields in ('id=>parent', 'id.parent'): for term in terms: _terms[term.term_id] = term.parent elif 'ids' == _fields: #for i,term in enumerate(terms): # _terms[i] = int(term.term_id) for term in terms: _terms[None] = int(term.term_id) elif 'tt_ids' == _fields: for term in terms: _terms[None] = int(term.term_taxonomy_id) elif 'names' == _fields: #for i,term in enumerate(terms): # _terms[i] = term.name for term in terms: _terms[None] = term.name elif 'slug' == _fields: for term in terms: _terms[None] = term.slug #elif 'id=>name' == _fields: elif _fields in ('id=>name', 'id.name'): for term in terms: _terms[term.term_id] = term.name #elif 'id=>slug' == _fields: elif _fields in ('id=>slug', 'id.slug'): for term in terms: _terms[term.term_id] = term.slug if _terms: # if not Php.empty(locals(), '_terms'): terms = _terms # Hierarchical queries are not limited, so 'offset' and 'number' must be handled now. if hierarchical and number and Php.is_array(terms): if offset >= len(terms): terms = array() # array() else: terms = Php.array_slice(terms, offset, number, True) WiCa.wp_cache_add(cache_key, terms, 'terms', WpC.WB.Wj.DAY_IN_SECONDS) if 'all' == _fields or 'all_with_object_id' == _fields: terms = Php.array_map(WiTx.get_term, terms) self.terms = terms return self.terms
def get_sql_for_clause(self, clause, parent_query, clause_key=''): ''' Generate SQL JOIN and WHERE clauses for a first-order query clause. "First-order" means that it's an array with a 'key' or 'value'. @global wpdb wpdb WordPress database abstraction object. @param array clause Query clause, passed by reference. @param array parent_query Parent query array. @param string clause_key Optional. The array key used to name the clause in the original `meta_query` parameters. If not provided, a key will be generated automatically @return array { Array containing JOIN & WHERE SQL clauses to append to a 1st-order query @type string join SQL fragment to append to the main JOIN clause. @type string where SQL fragment to append to the main WHERE clause. } @return array clause since clause passed by ref, need to return clause ''' wpdb = WpC.WB.Wj.wpdb # global wpdb sql_chunks = array( ('where', array()), ('join', array()), ) if Php.isset(clause, 'compare'): clause['compare'] = clause['compare'].upper() else: clause['compare'] = 'IN' if (Php.isset( clause, 'value') and Php.is_array(clause['value'])) else '=' if (not Php.in_array( clause['compare'], array('=', '!=', '>', '>=', '<', '<=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN', 'EXISTS', 'NOT EXISTS', 'REGEXP', 'NOT REGEXP', 'RLIKE'))): clause['compare'] = '=' meta_compare = clause['compare'] # First build the JOIN clause, if one is required. join = '' # We prefer to avoid joins if possible. Look for an existing join compatible with this clause. alias = self.find_compatible_table_alias(clause, parent_query) if False is alias: i = len(self.table_aliases) alias = 'mt' + i if i else self.meta_table # JOIN clauses for NOT EXISTS have their own syntax. if 'NOT EXISTS' == meta_compare: join += " LEFT JOIN self.meta_table" join += " AS " + alias if i else '' join += wpdb.prepare( " ON (self.primary_table.self.primary_id_column = {} AND {} = %s )" .format(alias.self.meta_id_column, alias.meta_key), clause['key']) # All other JOIN clauses. else: join += " INNER JOIN self.meta_table" join += " AS alias" if i else '' join += " ON ( self.primary_table.self.primary_id_column = {} )".format( alias.self.meta_id_column) self.table_aliases[None] = alias sql_chunks['join'][None] = join # Save the alias to this clause, for future siblings to find. clause['alias'] = alias # Determine the data type. _meta_type = clause['type'] if Php.isset(clause, 'type') else '' meta_type = self.get_cast_for_type(_meta_type) clause['cast'] = meta_type # Fallback for clause keys is the table alias. Key must be a string. if is_int(clause_key) or not clause_key: clause_key = clause['alias'] # Ensure unique clause keys, so none are overwritten. iterator = 1 clause_key_base = clause_key while Php.isset(self.clauses, clause_key): clause_key = clause_key_base + '-' + iterator iterator += 1 # Store the clause in our flat array. #self.clauses[ clause_key ] =& clause # =& is assignment by reference, "means that both vars pointing at the # same data, and nothing is copied anywhere" self.clauses[ clause_key] = clause # py array or {} are mutable same obj # Next, build the WHERE clause. # meta_key. if 'key' in clause: if 'NOT EXISTS' == meta_compare: sql_chunks['where'][ None] = alias + '.' + self.meta_id_column + ' IS NULL' else: sql_chunks['where'][None] = wpdb.prepare( "alias.meta_key = %s", trim(clause['key'])) # meta_value. if 'value' in clause: meta_value = clause['value'] if meta_compare in ('IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN'): if not Php.is_array(meta_value): meta_value = preg_split('/[,\s]+/', meta_value) else: meta_value = trim(meta_value) #switch ( meta_compare ) { if meta_compare in ('IN', 'NOT IN'): meta_compare_string = '(' + substr( str_repeat(',%s', len(meta_value)), 1) + ')' where = wpdb.prepare(meta_compare_string, meta_value) elif meta_compare in ('BETWEEN', 'NOT BETWEEN'): meta_value = Php.array_slice(meta_value, 0, 2) where = wpdb.prepare('%s AND %s', meta_value) elif meta_compare in ('LIKE', 'NOT LIKE'): meta_value = '%' + wpdb.esc_like(meta_value) + '%' where = wpdb.prepare('%s', meta_value) # EXISTS with a value is interpreted as '='. elif meta_compare == 'EXISTS': meta_compare = '=' where = wpdb.prepare('%s', meta_value) # 'value' is ignored for NOT EXISTS. elif meta_compare == 'NOT EXISTS': where = '' else: where = wpdb.prepare('%s', meta_value) if where: if 'CHAR' == meta_type: sql_chunks['where'][None] = "{} {} {}".format( alias.meta_value, meta_compare, where) else: sql_chunks['where'][None] = "CAST({} AS {}) {} {}".format( alias.meta_value, meta_type, meta_compare, where) # Multiple WHERE clauses (for meta_key and meta_value) should # be joined in parentheses. if 1 < len(sql_chunks['where']): sql_chunks['where'] = array( '( ' + Php.implode(' AND ', sql_chunks['where']) + ' )') #return sql_chunks return sql_chunks, clause #since clause passed by ref, need to return
def get_sql_for_query(self, query, depth=0): ''' Generate SQL clauses for a single query array. If nested subqueries are found, this method recurses the tree to produce the properly nested SQL. @param array query Query to parse, passed by reference. @param int depth Optional. Number of tree levels deep we currently are. Used to calculate indentation. Default 0. @return array { Array containing JOIN and WHERE SQL clauses to append to a single query array. @type string join SQL fragment to append to the main JOIN clause. @type string where SQL fragment to append to the main WHERE clause. } ''' sql_chunks = array( ('join', array()), ('where', array()), ) sql = array( ('join', ''), ('where', ''), ) indent = '' #for ( i = 0; i < depth; i++ ) { for i in range(depth): indent += " " for key, clause in query.items(): if 'relation' == key: relation = query['relation'] elif Php.is_array(clause): # This is a first-order clause. if self.is_first_order_clause(clause): clause_sql = self.get_sql_for_clause(clause, query, key) where_count = len(clause_sql['where']) if not where_count: sql_chunks['where'][None] = '' elif 1 is where_count: sql_chunks['where'][None] = clause_sql['where'][0] else: sql_chunks['where'][None] = '( ' + Php.implode( ' AND ', clause_sql['where']) + ' )' sql_chunks['join'] = Php.array_merge( sql_chunks['join'], clause_sql['join']) # This is a subquery, so we recurse. else: clause_sql = self.get_sql_for_query(clause, depth + 1) sql_chunks['where'][None] = clause_sql['where'] sql_chunks['join'][None] = clause_sql['join'] # Filter to remove empties. sql_chunks['join'] = Php.array_filter(sql_chunks['join']) sql_chunks['where'] = Php.array_filter(sql_chunks['where']) if Php.empty(locals(), 'relation'): relation = 'AND' # Filter duplicate JOIN clauses and combine into a single string. if not Php.empty(sql_chunks, 'join'): sql['join'] = Php.implode(' ', Php.array_unique(sql_chunks['join'])) # Generate a single WHERE clause with proper brackets and indentation. if not Php.empty(sql_chunks, 'where'): sql['where'] = '( ' + "\n " + indent + Php.implode( ' ' + "\n " + indent + relation + ' ' + "\n " + indent, sql_chunks['where']) + "\n" + indent + ')' return sql