<commit_before><?php
/**
 * PHP Version 5.4
 *
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 * @link          http://cakephp.org CakePHP(tm) Project
 * @since         CakePHP(tm) v 3.0.0
 * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
 */
namespace Cake\ORM;

use Cake\Collection\Iterator\MapReduce;
use Cake\Database\Query as DatabaseQuery;
use Cake\Database\Statement\BufferedStatement;
use Cake\Database\Statement\CallbackStatement;
use Cake\Event\Event;

/**
 * Extends the base Query class to provide new methods related to association
 * loading, automatic fields selection, automatic type casting and to wrap results
 * into an specific iterator that will be responsible for hydrating results if
 * required.
 *
 */
class Query extends DatabaseQuery {

/**
 * Instance of a table object this query is bound to
 *
 * @var \Cake\ORM\Table
 */
	protected $_table;

/**
 * Nested array describing the association to be fetched
 * and the options to apply for each of them, if any
 *
 * @var \ArrayObject
 */
	protected $_containments;

/**
 * Contains a nested array with the compiled containments tree
 * This is a normalized version of the user provided containments array.
 *
 * @var array
 */
	protected $_normalizedContainments;

/**
 * Whether the user select any fields before being executed, this is used
 * to determined if any fields should be automatically be selected.
 *
 * @var boolean
 */
	protected $_hasFields;

/**
 * A list of associations that should be eagerly loaded
 *
 * @var array
 */
	protected $_loadEagerly = [];

/**
 * List of options accepted by associations in contain()
 * index by key for faster access
 *
 * @var array
 */
	protected $_containOptions = [
		'associations' => 1,
		'foreignKey' => 1,
		'conditions' => 1,
		'fields' => 1,
		'sort' => 1,
		'matching' => 1,
		'queryBuilder' => 1
	];

/**
 * A ResultSet.
 *
 * When set, query execution will be bypassed.
 *
 * @var Cake\ORM\ResultSet
 * @see setResult()
 */
	protected $_results;

/**
 * Boolean for tracking whether or not buffered results
 * are enabled.
 *
 * @var boolean
 */
	protected $_useBufferedResults = true;

/**
 * List of map-reduce routines that should be applied over the query
 * result
 *
 * @var array
 */
	protected $_mapReduce = [];

/**
 * Holds any custom options passed using applyOptions that could not be processed
 * by any method in this class.
 *
 * @var array
 */
	protected $_options = [];

/**
 * Whether to hydrate results into entity objects
 *
 * @var boolean
 */
	protected $_hydrate = true;

/**
 * @param Cake\Database\Connection $connection
 * @param Cake\ORM\Table $table
 */
	public function __construct($connection, $table) {
		$this->connection($connection);
		$this->repository($table);
	}

/**
 * Returns the default table object that will be used by this query,
 * that is, the table that will appear in the from clause.
 *
 * When called with a Table argument, the default table object will be set
 * and this query object will be returned for chaining.
 *
 * @param \Cake\ORM\Table $table The default table object to use
 * @return \Cake\ORM\Table|Query
 */
	public function repository(Table $table = null) {
		if ($table === null) {
			return $this->_table;
		}
		$this->_table = $table;
		$this->addDefaultTypes($table);
		return $this;
	}

/**
 * Hints this object to associate the correct types when casting conditions
 * for the database. This is done by extracting the field types from the schema
 * associated to the passed table object. This prevents the user from repeating
 * himself when specifying conditions.
 *
 * This method returns the same query object for chaining.
 *
 * @param \Cake\ORM\Table $table
 * @return Query
 */
	public function addDefaultTypes(Table $table) {
		$alias = $table->alias();
		$schema = $table->schema();
		$fields = [];
		foreach ($schema->columns() as $f) {
			$fields[$f] = $fields[$alias . '.' . $f] = $schema->columnType($f);
		}
		$this->defaultTypes($this->defaultTypes() + $fields);

		return $this;
	}

/**
 * Sets the list of associations that should be eagerly loaded along with this
 * query. The list of associated tables passed must have been previously set as
 * associations using the Table API.
 *
 * ### Example:
 *
 * {{{
 *	// Bring articles' author information
 *	$query->contain('Author');
 *
 *	// Also bring the category and tags associated to each article
 *	$query->contain(['Category', 'Tag']);
 * }}}
 *
 * Associations can be arbitrarily nested using arrays, this allows this object to
 * calculate joins or any additional queries that must be executed to bring the
 * required associated data.
 *
 * ### Example:
 *
 * {{{
 *	// Eager load the product info, and for each product load other 2 associations
 *	$query->contain(['Product' => ['Manufacturer', 'Distributor']);
 *
 *	// For an author query, load his region, state and country
 *	$query->contain(['Region' => ['State' => 'Country']);
 * }}}
 *
 * Each association might define special options when eager loaded, the allowed
 * options that can be set per association are:
 *
 * - foreignKey: Used to set a different field to match both tables, if set to false
 *   no join conditions will be generated automatically
 * - conditions: An array of conditions that will be passed to either a query or
 *   join conditions. See `where` method for the valid format.
 * - fields: An array with the fields that should be fetched from the association
 * - sort: for associations that are not joined directly, the order they should
 *   appear in the resulting set
 * - matching: A boolean indicating if the parent association records should be
 *   filtered by those matching the conditions in the target association.
 *
 * ### Example:
 *
 * {{{
 *  // Set options for the articles that will be eagerly loaded for an author
 *	$query->contain([
 *		'Article' => [
 *			'field' => ['title'],
 *			'conditions' => ['read_count >' => 100],
 *			'sort' => ['published' => 'DESC']
 *		]
 *	]);
 *
 *	// Use special join conditions for getting an article author's 'likes'
 *	$query->contain([
 *		'Like' => [
 *			'foreignKey' => false,
 *			'conditions' => ['Article.author_id = Like.user_id']
 *		]
 *	]);
 *
 *	// Bring only articles that were tagged with 'cake'
 *	$query->contain([
 *		'Tag' => [
 *			'matching' => true,
 *			'conditions' => ['Tag.name' => 'cake']
 *		]
 *	]);
 * }}}
 *
 * If called with no arguments, this function will return an ArrayObject with
 * with the list of previously configured associations to be contained in the
 * result. This object can be modified directly as the reference is kept inside
 * the query.
 *
 * The resulting ArrayObject will always have association aliases as keys, and
 * options as values, if no options are passed, the values will be set to an empty
 * array
 *
 * ### Example:
 *
 * {{{
 *	// Set some associations
 *	$query->contain(['Article', 'Author' => ['fields' => ['Author.name']);
 *
 *  // Let's now add another field to Author
 *	$query->contain()['Author']['fields'][] = 'Author.email';
 *
 *	// Let's also add Article's tags
 *	$query->contain()['Article']['Tag'] = [];
 * }}}
 *
 * Please note that when modifying directly the containments array, you are
 * required to maintain the structure. That is, association names as keys
 * having array values. Failing to do so will result in an error
 *
 * If called with an empty first argument and $override is set to true, the
 * previous list will be emptied.
 *
 * @param array|string $associations list of table aliases to be queried
 * @param boolean $override whether override previous list with the one passed
 * defaults to merging previous list with the new one.
 * @return \ArrayObject|Query
 */
	public function contain($associations = null, $override = false) {
		if ($this->_containments === null || $override) {
			$this->_dirty();
			$this->_containments = new \ArrayObject;
		}

		if ($associations === null) {
			return $this->_containments;
		}

		$associations = (array)$associations;
		if (isset(current($associations)['instance'])) {
			$this->_containments = $this->_normalizedContainments = $associations;
			return $this;
		}

		$old = $this->_containments->getArrayCopy();
		$associations = $this->_reformatContain($associations, $old);
		$this->_containments->exchangeArray($associations);
		$this->_normalizedContainments = null;
		$this->_dirty();
		return $this;
	}

/**
 * Formats the containments array so that associations are always set as keys
 * in the array. This function merges the original associations array with
 * the new associations provided
 *
 * @param array $associations user provided containments array
 * @param array $original The original containments array to merge
 * with the new one
 * @return array
 */
	protected function _reformatContain($associations, $original) {
		$result = $original;<commit_msg>Adding Query::matching as an easier way to add filtered containments<commit_after><?php
/**
 * PHP Version 5.4
 *
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 * @link          http://cakephp.org CakePHP(tm) Project
 * @since         CakePHP(tm) v 3.0.0
 * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
 */
namespace Cake\ORM;

use Cake\Collection\Iterator\MapReduce;
use Cake\Database\Query as DatabaseQuery;
use Cake\Database\Statement\BufferedStatement;
use Cake\Database\Statement\CallbackStatement;
use Cake\Event\Event;

/**
 * Extends the base Query class to provide new methods related to association
 * loading, automatic fields selection, automatic type casting and to wrap results
 * into an specific iterator that will be responsible for hydrating results if
 * required.
 *
 */
class Query extends DatabaseQuery {

/**
 * Instance of a table object this query is bound to
 *
 * @var \Cake\ORM\Table
 */
	protected $_table;

/**
 * Nested array describing the association to be fetched
 * and the options to apply for each of them, if any
 *
 * @var \ArrayObject
 */
	protected $_containments;

/**
 * Contains a nested array with the compiled containments tree
 * This is a normalized version of the user provided containments array.
 *
 * @var array
 */
	protected $_normalizedContainments;

/**
 * Whether the user select any fields before being executed, this is used
 * to determined if any fields should be automatically be selected.
 *
 * @var boolean
 */
	protected $_hasFields;

/**
 * A list of associations that should be eagerly loaded
 *
 * @var array
 */
	protected $_loadEagerly = [];

/**
 * List of options accepted by associations in contain()
 * index by key for faster access
 *
 * @var array
 */
	protected $_containOptions = [
		'associations' => 1,
		'foreignKey' => 1,
		'conditions' => 1,
		'fields' => 1,
		'sort' => 1,
		'matching' => 1,
		'queryBuilder' => 1
	];

/**
 * A ResultSet.
 *
 * When set, query execution will be bypassed.
 *
 * @var Cake\ORM\ResultSet
 * @see setResult()
 */
	protected $_results;

/**
 * Boolean for tracking whether or not buffered results
 * are enabled.
 *
 * @var boolean
 */
	protected $_useBufferedResults = true;

/**
 * List of map-reduce routines that should be applied over the query
 * result
 *
 * @var array
 */
	protected $_mapReduce = [];

/**
 * Holds any custom options passed using applyOptions that could not be processed
 * by any method in this class.
 *
 * @var array
 */
	protected $_options = [];

/**
 * Whether to hydrate results into entity objects
 *
 * @var boolean
 */
	protected $_hydrate = true;

/**
 * @param Cake\Database\Connection $connection
 * @param Cake\ORM\Table $table
 */
	public function __construct($connection, $table) {
		$this->connection($connection);
		$this->repository($table);
	}

/**
 * Returns the default table object that will be used by this query,
 * that is, the table that will appear in the from clause.
 *
 * When called with a Table argument, the default table object will be set
 * and this query object will be returned for chaining.
 *
 * @param \Cake\ORM\Table $table The default table object to use
 * @return \Cake\ORM\Table|Query
 */
	public function repository(Table $table = null) {
		if ($table === null) {
			return $this->_table;
		}
		$this->_table = $table;
		$this->addDefaultTypes($table);
		return $this;
	}

/**
 * Hints this object to associate the correct types when casting conditions
 * for the database. This is done by extracting the field types from the schema
 * associated to the passed table object. This prevents the user from repeating
 * himself when specifying conditions.
 *
 * This method returns the same query object for chaining.
 *
 * @param \Cake\ORM\Table $table
 * @return Query
 */
	public function addDefaultTypes(Table $table) {
		$alias = $table->alias();
		$schema = $table->schema();
		$fields = [];
		foreach ($schema->columns() as $f) {
			$fields[$f] = $fields[$alias . '.' . $f] = $schema->columnType($f);
		}
		$this->defaultTypes($this->defaultTypes() + $fields);

		return $this;
	}

/**
 * Sets the list of associations that should be eagerly loaded along with this
 * query. The list of associated tables passed must have been previously set as
 * associations using the Table API.
 *
 * ### Example:
 *
 * {{{
 *	// Bring articles' author information
 *	$query->contain('Author');
 *
 *	// Also bring the category and tags associated to each article
 *	$query->contain(['Category', 'Tag']);
 * }}}
 *
 * Associations can be arbitrarily nested using arrays, this allows this object to
 * calculate joins or any additional queries that must be executed to bring the
 * required associated data.
 *
 * ### Example:
 *
 * {{{
 *	// Eager load the product info, and for each product load other 2 associations
 *	$query->contain(['Product' => ['Manufacturer', 'Distributor']);
 *
 *	// For an author query, load his region, state and country
 *	$query->contain(['Region' => ['State' => 'Country']);
 * }}}
 *
 * Each association might define special options when eager loaded, the allowed
 * options that can be set per association are:
 *
 * - foreignKey: Used to set a different field to match both tables, if set to false
 *   no join conditions will be generated automatically
 * - conditions: An array of conditions that will be passed to either a query or
 *   join conditions. See `where` method for the valid format.
 * - fields: An array with the fields that should be fetched from the association
 * - sort: for associations that are not joined directly, the order they should
 *   appear in the resulting set
 * - matching: A boolean indicating if the parent association records should be
 *   filtered by those matching the conditions in the target association.
 *
 * ### Example:
 *
 * {{{
 *  // Set options for the articles that will be eagerly loaded for an author
 *	$query->contain([
 *		'Article' => [
 *			'field' => ['title'],
 *			'conditions' => ['read_count >' => 100],
 *			'sort' => ['published' => 'DESC']
 *		]
 *	]);
 *
 *	// Use special join conditions for getting an article author's 'likes'
 *	$query->contain([
 *		'Like' => [
 *			'foreignKey' => false,
 *			'conditions' => ['Article.author_id = Like.user_id']
 *		]
 *	]);
 *
 * If called with no arguments, this function will return an ArrayObject with
 * with the list of previously configured associations to be contained in the
 * result. This object can be modified directly as the reference is kept inside
 * the query.
 *
 * The resulting ArrayObject will always have association aliases as keys, and
 * options as values, if no options are passed, the values will be set to an empty
 * array
 *
 * ### Example:
 *
 * {{{
 *	// Set some associations
 *	$query->contain(['Article', 'Author' => ['fields' => ['Author.name']);
 *
 *  // Let's now add another field to Author
 *	$query->contain()['Author']['fields'][] = 'Author.email';
 *
 *	// Let's also add Article's tags
 *	$query->contain()['Article']['Tag'] = [];
 * }}}
 *
 * Please note that when modifying directly the containments array, you are
 * required to maintain the structure. That is, association names as keys
 * having array values. Failing to do so will result in an error
 *
 * If called with an empty first argument and $override is set to true, the
 * previous list will be emptied.
 *
 * @param array|string $associations list of table aliases to be queried
 * @param boolean $override whether override previous list with the one passed
 * defaults to merging previous list with the new one.
 * @return \ArrayObject|Query
 */
	public function contain($associations = null, $override = false) {
		if ($this->_containments === null || $override) {
			$this->_dirty();
			$this->_containments = new \ArrayObject;
		}

		if ($associations === null) {
			return $this->_containments;
		}

		$associations = (array)$associations;
		if (isset(current($associations)['instance'])) {
			$this->_containments = $this->_normalizedContainments = $associations;
			return $this;
		}

		$old = $this->_containments->getArrayCopy();
		$associations = $this->_reformatContain($associations, $old);
		$this->_containments->exchangeArray($associations);
		$this->_normalizedContainments = null;
		$this->_dirty();
		return $this;
	}

/**
 * Adds filtering conditions to this query to only bring rows that have a relation
 * to another from an associated table, based on conditions in the associated table.
 *
 * This function will add entries in the ``contain`` graph.
 *
 * ### Example:
 *
 * {{{
 *  // Bring only articles that were tagged with 'cake'
 *	$query->matching('Tags', function($q) {
 *		return $q->where(['name' => 'cake']);
 *	);
 * }}}
 *
 * It is possible to filter by deep associations by using dot notation:
 *
 * ### Example:
 *
 * {{{
 *  // Bring only articles that were commented by 'markstory'
 *	$query->matching('Comments.Users', function($q) {
 *		return $q->where(['username' => 'markstory']);
 *	);
 * }}}
 *
 * As this function will create ``INNER JOIN``, you might want to consider
 * calling ``distinct`` on this query as you might get duplicate rows if
 * your conditions don't filter them already. This might be the case, for example,
 * of the same user commenting more than once in the same article.
 *
 * ### Example:
 *
 * {{{
 *  // Bring unique articles that were commented by 'markstory'
 *	$query->distinct(['Articles.id'])
 *	->matching('Comments.Users', function($q) {
 *		return $q->where(['username' => 'markstory']);
 *	);
 * }}}
 *
 * Please note that the query passed to the closure will only accept calling
 * ``select``, ``where``, ``andWhere`` and ``orWhere`` on it. If you wish to
 * add more complex clauses you can do it directly in the main query.
 *
 * @param string $assoc The association to filter by
 * @param callable $builder a function that will receive a pre-made query object
 * that can be used to add custom conditions or selecting some fields
 * @return Query
 */
	public function matching($assoc, callable $builder = null) {
		$options = ['queryBuilder' => $builder, 'matching' => true];
		return $this->contain([$assoc => array_filter($options)]);
	}

/**
 * Formats the containments array so that associations are always set as keys
 * in the array. This function merges the original associations array with
 * the new associations provided
 *
 * @param array $associations user provided containments array
 * @param array $original The original containments array to merge
 * with the new one
 * @return array
 */
	protected function _reformatContain($associations, $original) {
		$result = $original;
{'fraction_non_alphanumeric': 0.12091600039482775, 'fraction_numerical': 0.0015793110255650973, 'mean_word_length': 3.715615545729579, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 132, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '761915', 'n_tokens_mistral': 5894, 'n_tokens_neox': 5643, 'n_words': 2282}