<commit_before><?php
namespace Darya\Database;

use Darya\Database\DatabaseInterface;
use Darya\Storage\Readable;
use Darya\Storage\Modifiable;

class Storage implements Readable, Modifiable {
	
	/**
	 * @var \Darya\Database\DatabaseInterface
	 */
	protected $connection;
	
	/**
	 * @var array
	 */
	protected $operators = array('>=', '<=', '>', '<', '=', '!=', '<>', 'in', 'not in', 'is', 'is not', 'like', 'not like');
	
	public function __construct(DatabaseInterface $connection) {
		$this->connection = $connection;
	}
	
	/**
	 * Prepare an individual filter condition.
	 * 
	 * @param string       $column
	 * @param array|string $value
	 * @return string
	 */
	protected function prepareFilter($column, $value) {
		list($column, $operator) = explode(' ', $column, 2);
		$column = $this->connection->escape($column);
		$operator = in_array(strtolower($operator), $this->operators) ? $operator : '=';
		
		if (is_array($value)) {
			$value = array_map(array($this->connection, 'escape'), $value);
			$value = "('" . implode("','", $value) . "')";
			
			if ($operator === '=') {
				$operator = 'IN';
			}
		} else {
			$value = "'" . $this->connection->escape($value) . "'";
		}
		
		return "$column $operator $value";
	}
	
	protected function prepareWhere($filter, $comparison = 'AND') {
		$conditions = array();
		
		foreach ($filter as $column => $value) {
			$conditions[] = $this->prepareFilter($column, $value);
		}
		
		return implode(" $comparison ", $conditions);
	}
	
	public function read($table, $filter = array(), $order = null, $limit = null, $offset = 0) {
		$where = $this->prepareWhere($filter);
		
		$query = "SELECT * FROM $table";
		
		if ($where) {
			$query .= "WHERE $where";
		}
		
		return $this->connection->query($query);
	}
	
}<commit_msg>Completed readable database storage implementation.<commit_after><?php
namespace Darya\Database;

use Darya\Database\DatabaseInterface;
use Darya\Storage\Readable;
use Darya\Storage\Modifiable;

class Storage implements Readable {
	
	/**
	 * @var \Darya\Database\DatabaseInterface
	 */
	protected $connection;
	
	/**
	 * @var array
	 */
	protected $operators = array('>=', '<=', '>', '<', '=', '!=', '<>', 'in', 'not in', 'is', 'is not', 'like', 'not like');
	
	public function __construct(DatabaseInterface $connection) {
		$this->connection = $connection;
	}
	
	/**
	 * Prepare an individual filter condition.
	 * 
	 * @param string       $column
	 * @param array|string $value
	 * @return string
	 */
	protected function prepareFilter($column, $value) {
		list($column, $operator) = array_pad(explode(' ', $column, 2), 2, null);
		$column = $this->connection->escape($column);
		$operator = in_array(strtolower($operator), $this->operators) ? $operator : '=';
		
		if (is_array($value)) {
			$value = array_map(array($this->connection, 'escape'), $value);
			$value = "('" . implode("','", $value) . "')";
			
			if ($operator === '=') {
				$operator = 'IN';
			}
		} else {
			$value = "'" . $this->connection->escape($value) . "'";
		}
		
		return "$column $operator $value";
	}
	
	protected function prepareWhere(array $filter, $comparison = 'AND') {
		$conditions = array();
		
		foreach ($filter as $column => $value) {
			$conditions[] = $this->prepareFilter($column, $value);
		}
		
		return count($conditions) ? 'WHERE ' . implode(" $comparison ", $conditions) : null;
	}
	
	protected function prepareOrder($column, $direction = null) {
		$column = $this->connection->escape($column);
		$direction = !is_null($direction) ? $this->connection->escape($direction) : 'ASC';
		
		return !empty($column) ? "$column $direction" : null;
	}
	
	protected function prepareOrderBy($order) {
		$conditions = array();
		
		foreach ((array) $order as $key => $value) {
			if (is_numeric($key)) {
				$conditions[] = $this->prepareOrder($value);
			} else {
				$conditions[] = $this->prepareOrder($key, $value);
			}
		}
		
		return count($conditions) ? 'ORDER BY ' . implode(', ', $conditions) : null;
	}
	
	protected function prepareSelect($columns, $table, $where = null, $order = null, $limit = null) {
		$columns = is_array($columns) ? implode(', ', $columns) : $columns;
		$query = "SELECT $columns FROM $table";
		
		foreach (array($where, $order, $limit) as $clause) {
			if (!empty($clause)) {
				$query .= " $clause";
			}
		}
		
		return $query;
	}
	
	public function read($table, array $filter = array(), $order = null, $limit = null, $offset = 0) {
		$query = $this->prepareSelect('*', $table, $this->prepareWhere($filter), $this->prepareOrderBy($order));
		
		return $this->connection->query($query);
	}
	
	public function count($table, array $filter = array(), $order = null, $limit = null, $offset = 0) {
		$query = $this->prepareSelect('1', $table, $this->prepareWhere($filter), $this->prepareOrderBy($order));
		
		return count($this->connection->query($query));
	}
	
}
{'fraction_non_alphanumeric': 0.19971175622812434, 'fraction_numerical': 0.001441218859378217, 'mean_word_length': 3.917004048582996, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 47, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5754167', 'n_tokens_mistral': 1745, 'n_tokens_neox': 1524, 'n_words': 355}