Посмотрел я тему кружка любопытных извращенцев, вспомнил свою практику и подумал, что часто приходится делать такую рутинную работу, как генерация тестовых данных - строк и массивов. И если генераторы строк уже давным давно написаны, то генераторы массивов пишутся прям во время теста, что, во-первых, загромождает код теста, во-вторых часто пишется достаточно лениво и потому массивы получаются похожи на array("блабла", "йцукен", "11111"), в-третьих это совершенно неправильно. Уверен, что генерация массивов - это один из немногих примеров, которые впадлу завернуть в какую-то обертку, потому что "только для тестов". Предлагаю вам библиотеку - PHP Tests Helper. Её цель - помочь разработчику в проведении теста части своего продукта, или либы. Лицензия - LGPL. [ Скачать с Google Code ] [ Краткий ман ] На данный момент есть три функции, с очень расширенными настройками - генерация строки, генерация массива, создание sql-запроса на заполнение таблицы данными. Ну и для затравки примеры: PHP: <?php $th = new TestsHelper; $arr = $th->createArray(array( 'length' => array(2, 5), // Массивы длинной от 2 от 5 элементов 'depth' => array(0, 1), // Вложеностью до 1 'keys' => array( 'length' => array(5, 10), // Длина ключей - 5-10 символов 'chars' => StringGenerator::CYR | StringGenerator::NUM // Кириллица и цифры ), 'values' => 'v*' // В качестве значений элементов подставить 'v' + порядковый номер элемента )); Код (Text): Array ( [ійЧЩкип5] => v0 [жЛ9ШТ] => Array ( [кфІ6РчжЩР] => v0 [Ё2М1м] => v1 [смшЗА6] => v2 [Ю4МьРЫкЩлО] => v3 ) [ЩЩМ2Уб] => v2 [фв7є0] => v3 [єПяцГз7Т4] => Array ( [Р1ыхлТЯ] => v0 [ЬЮфЦЩСЦ] => v1 ) ) PHP: <?php $th->createQuery(array( 'table' => 'messages', // В таблицу messages 'fields' => array ( 'ID' => null, // ID у нас autoincrement 'Text' => array ( 'length' => array (8, 64), // Длина текста - от 8 до 64 символов 'chars' => StringGenerator::LAT | StringGenerator::SPACES // Использовать латинские символы и пробелы ) ), 'length' => 100, // 100 записей 'execute' => true // Выполнять запросы по ходу их создания )); Подробнее - в инструкции и исходном коде Принимаю предложения и пожелания И да, предлагаю рекомендовать к использованию в кружке любопытных извращенцев
Выложил обновленную версию. Код в кодохранилище. Более подробная информация - в вики на гуглокоде. И легкий пример: PHP: <? $th->createQuery(array( 'table' => 'messages', // В таблицу messages 'fields' => array ( 'ID' => null, 'Text' => array ( 'length' => array (8, 64), 'chars' => StringGenerator::LAT | StringGenerator::SPACES ) ), 'length' => 100, 'execute' => true )); И еще расширил StringGenerator
И опять про sql. Было бы полезно иметь возможность генерить связанные данные для таблиц. Для проверки join.
дааамс.. это будет потяжелее, конечно. я подумаю над алгоритмом... если у кого есть предложения по интерфейсу и реализации - прошу.
TheShock Одно замечание по стилистике написания кода: В интерфейс не следует вносить объявление конструктора и/или деструктора. Интерфейс описывает логическую абстракцию методов объекта, конструктор же является служебным и, по сути, не является равноправным членом класса. У объекта, подобно человеку, есть две стадии: рождение и смерть. Для этих стадий мы можем определить какие-то действия, но, в любом случае, без этих действий невозможна суть существования, т.е определим мы конструктор или нет, существовать он всё равно будет. Поэтому стилистика объектно-ориентированного программирования не требует описания конструктора в интерфейсе, поскольку интерфейс оъбявляет методы, которые должны быть обязательно в имплементирующем классе, а конструктор и деструктор так и так существует на уровне объекта. Это только поправка.
Apple, спасибо.. Но ведь это не просто объявление конструктора, а указание, что каждый ITestsHelperGenerator должен обязательно иметь в конструкторе аргумент $config. Или все-равно не нужно?
Интерфейс лишь создаёт регламент методов, которые обязаны быть реализованы в классе. Конструктор так и так существует, опишем мы его или нет. Поэтому необходимости описывать ни сам конструктор, ни его аргументы — нет. Во всяком случае на одном иностранном форуме — DevNetworks — мы этот вопрос обсуждали. Большинство стояло на мнении, что конструктор не должен быть объявлен в интерфейсе, сколько бы аргументов он не принимал. Лично я придерживаюсь этого, поскольку человек волен распоряжаться конструктором всё-таки так, как он хочет. А интерфейс задаёт регламент обязательных методов класса, без которых объекта быть не может.
По идее описание конструктора в интерфейсе возможно. В тех случаях, когда мне необходимо создавать объект по конкретным данным или вообще без данных. Например, есть интерфейс "Поверхность" и тогда любой объект, реализующий этот интерфейс, должен иметь конструктор с параметрами длина и ширина. Или объект реализующий интерфейс "ПервоеЧтоПопалосьПодРукуДляБроска" должен иметь конструктор без аргументов. PS. Могу описать алгоритм для join'ов на словах.
расскажи свое мнение и, как ты считаешь, должен выглядеть интерфейс? Например связывание двух таблиц по полю. Как узнать какие АйДи были вставлены? Делать по одному запросу и получать mysql_last_id(), или предположить, что в таблице нету еще данных и принудительно указывать ID начиная с 1(или с числа, которое пользовать ввел в конфиге при конструировании)? Как реагировать, если пользователь связал две таблицы? Например Код (Text): $table1->link($table2, 'table2ID'); $table2->link($table1, 'table1ID');
Я не перестану повторять, что интерфейс задаёт методы, которые обязаны быть реализованы в объекте. Приведу пример даже: есть интерфейс, описывающий некую модель, возмем ручку. Какие функции должна реализовывать ручка? Ею можно писать, для этого внутри должен содержаться ещё один объект - стержень, который находится в оболочке и является low-level объектом по отношению к колпачку. Так вот интерфейс описания ручки мог вы выглядеть следующим образом: PHP: <? interface IPenCommon { public function Write($text); public function Flush(); } interface IBar implements IPenCommon { protected function FillInk($level); protected function SetInkColor($color); public function FlushInk($level); } interface IPen implements IBar { public function SetCat(); public function DropCap(); } ?> В данном примере мы может видеть методы FillInk и SetInkColor, которые определяет производитель ручки. Интерфейс описывает то, без чего не может существовать ручки, производитель может описать следующий класс реализации: PHP: <? class GelPen implements IPen { public function __construct($color, $level) { $this->SetInkColor($color); $this->FillInk($level); } /* Тут идут методы интерфейсов ... */ } ?> В конструктор передаются аргументы для производства ручки. Но что если производитель захочет сделать так: PHP: <? class GelPen implements IPen { public static $ob = null; public static function CreatePen($color, $level) { // Обращаемся к статическому свойству и пишем туда другой класс } /* Тут идут методы интерфейсов ... */ } ?> Т.е описывая конструктор в интерфейсе вы завязываете руки тому, кто реализовывает этот интерфейс. Интерфейс обязан описывать только те методы (и свойства), которые класс должен иметь для полноценной работы. Интерфейс — это не каша из методов, которые из-за прихоти туда запихнули, а очень кратко-структурированное объединение методов, описывающих свойства объекта, которые он будет иметь при себе. При этом не имеет значения, будет у класса конструктор, или как-то по-другому будут передаваться данные, интерфейсу срать на это, поскольку его цель в описании СТРУКТУРЫ ОБЪЕКТА, а конструктор таковым не является.
В отличии от многих других языков, в С++ нет чёткого понятия интерфейса или соотв. типа. Для этого мы создаёт абстрактный класс (кстати, что мне не нравится в С++, так это реализация класса возможна за его пределами), поэтому он может содержать как свойства, так и методы. Хотя в С++ — это общий шаблон проектирования, и интерфейсом называется лишь условно. Поскольку происходит разделение только на логическом уровне там, тут я описал некоторые члены интерфейса как СВОЙСТВА, а не просто методы.
Совершенно согласен. Конструктор в интерфейсе это редкое исключение из правила. его цель описание поведения объекта. А вообще я не сразу переключился на PHP. Т.к. в PHP для объекта возможен только один конструктор, то действительно:
Apple Да, явное объявление конструктора в интерфейсе - это завязывание рук. Вот в том случае его и объявляют, чтобы их завязать. Например, у меня есть объекты, которые получают данные только конструктором( почему так это отдельный вопрос), вот я в интерфейсе иго и объявил.
Работа с одной таблицей. Далее идут мысли по реализации, которые не проверены практикой. Думаю при столкновении с реальным миром многое может измениться. Работа с одной таблицей. Работа с одной таблицей имеет смысл, только если уже созданы таблицы с которыми мы хотим связать новую. 1. Вызов. 1.1 Вариант 1: добавляем поля в массив. PHP: <?php $th->createQuery(array( 'table' => 'messages', 'fields' => array ( 'ID' => null, 'Text' => array ( 'length' => array (8, 64), 'chars' => StringGenerator::LAT | StringGenerator::SPACES ), 'newTableId' => array ( 'chars' => StringGenerator::NUM, 'range' => array (0,30000), ), 'oldTableId' => array ( 'chars' => StringGenerator::NUM, 'range' => array (0,30000), ), ), 'length' => 100, 'execute' => true, 'links'=> array( 'param' => array( 'DNS' => '...', 'DB' => 'MyDB', 'takeFromDb' => true ), 0 => array( 0 => array( 'thisField' => 'newTabId', 'thatField' => 'id', 'type' => 'eq', 'linkTable' => 'newTable', 'column' => ... 'numLinks' => array(0, 2) ), 1 => array( 'thisField' => 'oldTabId', 'thatField' => 'id', 'type' => 'eq', 'linkTable' => 'oldTable', 'column' => ... 'numLinks' => array(0, 2) ), 'length' => 5 1 => array( 'thisField' => 'oldTabId', 'thatField' => 'id', 'type' => 'eq', 'linkTable' => 'oldTable', 'column' => ..., 'numLinks' => array(0, 2) 'length' => 10 ) ) ) ); ?> Как видно добавляется элемент links. Он содержит в себе массивы описывающие с какой таблицей связывать, по каким полям, каким образом, массив параметров (возможно имеет смысл перенести все это на объектную основу или выделить нумерованные элементы массива в отдельный массив). link[0] – описывает запись связанную с двумя таблицами и говорит что надо создать пять таких записей. link[1] – описывает запись связанную с одной таблицей. Параметры: DNS - строка подключения к БД DB - база данных из которой брать информацию takeFromDb - нужно ли брать данные из БД. При takeFromDb=false DNS и DB необязательны и наоборот. Я не знаю как ты организушь доступ к БД, возможно DNS и DB передавать не нужно. type определяет как связывать поля. Надо определить как его задавать. Мне приходят в голову следующие варианты: а) eq - равно, lt - меньше, gt - больше, le - меньше или равно, ge - больше или равно. б) =, <, >, <=, >= в) тупо константами 1, 2. 3 и т.д. linkTable – необходимо при takeFromDb=true, чтобы знать из какой таблицы брать данные. column – необходимо при takeFromDb=false. Данные из 'thatField'. Скорее всего опять массив. length – количесто связанных записей. Надо решить либо это количество добавляется к общему числу записей в таблице (fullLength= length+links[0].length+ links[1].length +…), или уже включено в него (length – links[0].length – links[1].length – … = число несвязанных записей). Думаю первый вариант проще в реализации, но второй более удобен для использования. numLinks – количество связей с одним полем в конечной таблице (от и до). Если array(0), то случайно. Если первая цифра 0, то параметр length обязателен. В противном случае параметр length игнорируется. 1.2 Вариант 2: добавляем аргументы в функцию. Массив links становится вторым аргументом функции. 2.Проверки а) Проверяем соответствие длин (если решим включать длины друг в друга). б) Проверяем соответствие полей для связи полям описанным в массиве fields. в) Проверяем типы полей для связи на соответствие друг другу. г) Проверяем подключение к БД. Наличие нужной таблицы и нужных полей в таблице если takeFromDb=true д) Проверить не конфликтность numLinks для записей, которые одновременно связаны более чем с одной таблицей. PS. Приду домой продолжу.