Достаточно много можно видеть объектных оберток над стандартными примитивами в PHP. Но ни одна из тех, что я видел не могла быть использована в реальной ситуации. Даже встроенная ArrayObject очень скудна и навевает впечатление, что ее сделали "абы отстали с объектностью". Большинство оберток не умеют работать сами с собой, например: Код (Text): oArray('a', 'b', 'c')->merge(oArray('d', 'e')); (ничего не имею против автора и его демонстрационного класса, тем более, именно его творение подтолкнуло многих на написание подобных оберток) Также все обертки, которые я видел - реализовывали только базовый функционал, которого явно недостаточно, чтобы полноценно с ними работать. Ну и еще много недостатков. Я представляю альфа-версию полноценной обертки для объектной работы с примитивами: Object-oriented PHP Она лишена большинства недостатков известных оберток. Подробнее - на странице проекта. PHP: <?php $map ->join(' ') // String ->replace(',', 'зпт') ->changeCase(Str::TITLE, Str::UP_FORCED) ->insert('[вставлено]', 5) ->length() // Number ->multiply(4) ->add(6, 9, new Number(15)) ->divided(5) ->sum(Number::EVEN) ->dump() // (Number) 1062.51 ->root(4) ->round(3) // Just 3 symbols after dot ->dump() // (Number) 5.709 ->round(Number::UP) // round to up (ceil) ->hex('15abbf') ->toString() // String ('1420223') ->hash() // 'md5' as default ->dump(); // (String) '0d1b1558224c8f3b125cd905c378c9f7' Еще раз повторю - цель была не демонстрация возможностей PHP, а создании реальной обертки, которую вполне можно будет использовать в реальных проектах. Любителям поговорить о спичечной оптимизации в ущерб читабельности и грациозности кода прошу пройти сюда. Сразу предупреждаю за реплики алля "Зачем изобретать велосипед?", "ООП ради ООП" и "КГ/АМ" буду слать матом. Принимаю восторженные отзывы и конструктивную критику P.S. Спасибо Koc'y, что подтолкнул меня к этому. Сообщение на Хабрахабре
Это всё интересно, но без перегрузки операторов никогда бы классом Number пользоваться не стал. Вряд ли меня можно будет убедить, что нужно записывать так: PHP: <? $num = new Number(10); $num -> add(1); ?> а не так: PHP: <? $num = 10; $num++; ?> Это совсем простая операция. А если посложнее? PHP: <? $y = 5; $z = 3; $x = cos(8 * pow($y - 1, 3) - 3 * $z) / pow(18 * $y - $z, 2); echo $x; ?> PHP: <? $y = new Number(5); $z = new Number(3); $x = new Number( Number::create($y -> get()) -> subtract(1) -> power(3) -> multiply(8) -> subtract ( Number::create($z -> get()) -> multiply(3) -> get() ) -> cos() -> divided ( Number::create($y -> get()) -> multiply(18) -> subtract($z -> get()) -> power(2) -> get() ) -> get() ); echo $x -> get(); ?> Что читабельнее? Со String наоборот сложные операции выполнять удобнее, так что здесь поддерживаю. В Map особенно не вглядывался, но сама идея подобного класса мне нравится.
Много методов, прикольно). public function toInt () { return (int) $this->number; } Вроде лучше через + 0 приводить к целому, но я не уверен. Можно еще добавить ф-ции перевода из байт в кб, мб, ...
Такие вещи как changeCase или replace я бы реализовал в виде одного метода map или each в который передаётся имя callback функции, т.к. данные методы к массивам в принципе никакого отношения не имеют и только "раздувают" интерфейс.
[Обновлена версия. Исправленно несколько достаточно критических багов] PHP: <? $y = new Number(5); $z = new Number(3); $x = $y -> copy() -> subtract(1) -> power(3) -> multiply(8) -> subtract ( $z -> copy() -> multiply(3) ) -> cos() -> divided ( $y -> copy() -> multiply(18) -> subtract($z) -> power(2) ); echo $x; ?> Это, конечно, если надо оставить $y и $z неизменными Я так понял, что затруднения написать данный пример не возникли? Это хорошо. Он выдает тот же ответ, что и формула. Но, если честно, имхо, для человека намного приятнее читать именно цепочечный вызов тяжелой функции. Смотри сам: можно видеть что за чем выполняется даже при беглом просмотре. Функция не пугает, в отличии от: Код (Text): $x = cos(8 * pow($y - 1, 3) - 3 * $z) / pow(18 * $y - $z, 2); Потому что записана так, как читает человек, а не машина: Икс равно: игрик, отнять один, вознести в третью степень, умножить на 8, отнять 3 зеда, и косинунс поделить на (18 игриков, от которых отняли зед и получили с них квадрат). А кое-что ты даже не сделаешь без дополнительный некрасивых действий(дополнительное расширение функционала). Например, как ты получишь кубичный корень? А тут: Код (Text): $x->root(3); (против) pow($x, 1/3) Кстати, можно записать даже так: PHP: <?php $x = $y -> copy() -> subtract(1) -> power(3) -> multiply(8) -> subtract ($z->get() * 3) -> cos() -> divided ( $y -> copy() -> multiply(18) -> subtract($z) -> power(2) ); ?> Тем более, никто не заставляет, более того, даже не рекомендует работать с этим классом ВЕЗДЕ. Например, в циклах это просто не имеет смысла. Каждый из объектов, как я уже говорил готов принимать что себя, что встроенный примитив равнозначно. Можно писать: PHP: <?php $m = new Map(1, 2, 3); $i = new Number(2); $m[$i]; /* или */ $m[2]; // Работает равнозначно ?> Согласен, намного красивее писать $m[2], чем new Number(2); Думаю, чем тяжелее будут операции - тем красивее будет выглядеть именно обертка Тем не менее, основные цель создания класса Number были в том, чтобы можно было не прерывать цепочку - раз. Смело писать метод ->dump() не боясь, что там окажется integer или float - два. Я даже думал булево в объект забрать, но потом передумал ) Перезагрузки, к сожалению, без залезания в сырцы пхп не осуществить. +0, афаик, приводит просто к нужному типу ("1.234" + 0) приведет к флоату, а ("1" + 0) приведет к инту. Но спасибо, это тоже не помешает. Естественно, я буду расширять функционал. Функцию записал в пожелания
Ну вообще-то так и реализовано в core. Это просто алиасы для более удобной работы: PHP: <?php // Recursive str public function changeCase ($mode = self::VALUES, $strWhat = Str::ALL, $strMode = Str::UP) { $this->walk('Str::changeCase', $mode, array($strWhat, $strMode)); return $this; } public function filter ($mode = self::VALUES, $strMode, $add = false) { $this->walk('Str::filter', $mode, array($strMode, $add)); return $this; } Я вот правда не знаю, как сделать передачу параметров методу walk, если в функции ключевой элемент будет не первым. Например: Код (Text): public static function find($what, $where, Хотя данная функция врядли нуждается в рекурсивном маппинге, но ключевой элемент (который был бы элементом массива) тут второй.
А помойму это приведет только к числу, в том числе и с точкой. лучше так - (int)$var. Соглашусь с mantell. Зачем было городить столько сложностей на примитивы?
кстати, на счет Number. Имеет ли смысл заменить [add, subtract, multiply] на [plus, minus, times]? И, если имеет, на что заменить "divided" ?
kas1e, выше уже описал. Вполне возможно, что либа будет работать и без Number вовсе (если он вам очень мешает), возвращая примитив, если соответствующего класса не будет: Код (Text): return (is_numeric ($elem) and class_exists ('Number')) ? new Number($elem) : $elem Но я не знаю, как поведет себя строка Код (Text): $obj instanceof Number Имхо, с Number удобнее, но, если так мозолит глаз, то вполне могу протестировать и подогнать либу под работу без Number
Ну скоро мы доростём до PHP: <? Logic::if(Functions::create(Vars::getInstanse()->var)->noEmpty()->nois_Array())) ->do( PHP::OutPut("Hello Hell :p"); ) ->Elseif(Functions::create(Vars::getInstanse()->var)->is_array()) ->do( Logic::for(Vars::getInstanse()->var) ->do( Logic::if(Functions::create(Vars::getInstanse()->var)->noEmpty()->nois_Array()) ->do( PHP::OutPut("Array value:".Vars::getInstanse()->var."\r\n") ) ), PHP::OutPut("You See Hell Logic - FOR") ) ->Else( PHP::OutPut("Ohh... Some Error?") ); ?>
Просто если делать для удобство - то зачем заставлять юзера вместо PHP: $num = 10; писать PHP: $num = new Number(10); ? Это ведь на 12 символов больше. Да и по скорости, думаю, разница есть А в целом зачет.
зачем же "\r\n"? можно поставить PHP::OutPut("Array value:".Vars::getInstanse()->var)->newLine(); Koc, думаю, что вполне может быть удобно. Более того, можно унаследовать от Number класс PageLink, к примеру и сделать сразу метод для формирования ссылки. Алля. PHP: <?php class PageLink extends Number { public function formLink () { return "index.php?page=$this"; } } ?> Что-то типа того, в общем. kas1e, дык никто и не заставляет писать $num = new Number(10); ты пишешь это только тогда, когда это тебе нужно. Можно написать так: PHP: <?php $map = new Map(1, 2, 3); foreach ($map as $value) { // $value тут уже объект Number } ?>
[Обновлена версия] * Исправлено несколько ошибок, которые приводили к некорректной работе методов * Добавлен метод copy() во все объекты, чтобы работать с копией, не боясь повредить оригинал (например, как в формулах в предыдущих сообщениях и в примере): PHP: <?php $y = new Number(5); $z = new Number(3); $x = $y -> copy() -> power(3) -> subtract ( $z -> copy() -> multiply(3) ) -> divided ( $y -> copy() -> subtract($z) ); // $y и $z - остались неизменными + добавлен метод Map::isEmpty($mode), позволяющий проверить массив на пустоту стандартно ($mode = 0), рекурсивно, (нет ли каких-либо переменных, кроме массивов) ($mode = 1), рекурсивно (нет ли каких-либо непустых переменных) ($mode = 0) + добавлены String:reg* функции. Теперь pregMatch* возвращает массив найденных значений, а количество найденных можно получить через аргумент-ссылку. Проверить, было ли вообще что-то найдено можно с помощью аргумента-ссылки, или с помощью: PHP: <?php if ( $string->pregTest (/*args*/)) // найдено что-то if (!$string->pregMatch (/*args*/)->isEmpty()) // найдено что-то if (!$string->pregMatchAll (/*args*/)->isEmpty(1)) // найдено что-то * Отредактирован метод Number::toString() - теперь можно задать формат вывода числа: $num->toString(2, '.', ' '); а можно и не задавать. + добавлен метод Number::Convert (за счет чего класс Number стал намного интереснее сам по себе, а также - для наследования) PHP: <?php // Сколько байт в 300 гигабайтах? echo Number300)->convert('Bytes', 'GiB', 'B'), "\n"; // 322122547200 // Сколько секунд в 3 сутках? echo Number(3)->convert('Time', 'Day', 'Second'), "\n"; // 259200 // Сколько метров в одной морской миле? echo Number(1)->convert('Length', 'SeaMile', 'Metr'), "\n"; // 1852 PHP: <?php /* Класс KurrencyKonverter унаследован от Number, в нем отредактировано только одно свойство. * Подробнее - смотрите «KurrencyKonverter.php» в «Downloads» */ $uah = new KurrencyKonverter (500); // Сколько долларов у нас будет, если мы купим их в Украине за 500 гривень echo $uah ->copy() ->convert('Currency.Ua.Sell', 'UAH', 'USD') ->toString(2); // 63,02 // Сколько долларов у нас будет, если мы купим в Украине за 500 гривень рубли, а потом в России за эти же рубли - доллары echo $uah ->convert('Currency.Ua.Sell', 'UAH', 'RUR') ->convert('Currency.Ru' , 'RUR', 'USD') ->toString(2); // 44,77
конвертер миль/дат/байт жирр!! Валюты - тоже офигенски. // I know what is right - «CurrencyСonverter» class KurrencyKonverter extends Number { дык зочем написал иначе? как извне задать значения курсов? хотя че я спрашиваю, можно просто написать еще один метод. Чего тебе не спится в 5 часов утра-то? ))
TheShock В скайпе то появись По сабжу - это гут. Насколько уж я не люблю OOP ради OOP, но тут реализовано с чувством, толком и расстановкой. Typehinting правильно подмечен May I join you?
Я - заядлый KDEшник Действительно - можно написать дополнительный метод, или просто в конструкторе сразу поставить нужное значение курса, как я сделал в Конвертере. самый плодотворный период¸если честно. Приятно слышать. Конечно! Рад любой помощи. Хотя, в общем-то, сам справляюсь . Принимаю предложения. ПС. У меня возникла одна идея. Чуть позже воплощу в жизнь. Только с дотдеб пхп 5.3 поставлю.
[Обновлена версия] Сейчас: 0.0.1.3 Изменения: Добавил функции Number (); String (); Map (); по сути - это аналог методов Object::create() у соответсвующих объектах.К примеру, теперь можно так: PHP: <?php Map('a', 'b', 'c')->length(); ?> Методы Number::isEven(), Number::isOdd(), Number::rest($num) - число становится остатком от деления себя на $num Добавлен метод match во все объекты (см. пример ниже) Исправлены кое-какие мелкие ошибки в коде. И самое интересное: добавлена расширенная возможность работать с новыми возможностями php 5.3 , а именно с лямбда-функциями (при этом, если их не использовать, код совместим с более старыми версиями). Примеры: PHP: <?php $n = new Number(115); if($n->match(function ($t) { return ($t->root(3)->gt(5)); // Если кубический корень числа больше 5 - истина, иначе - ложь })) { echo '$n is greater than 125', "\n"; } else { echo '$n is smaller or equal than 125', "\n"; } $s = new String("Some string with number(e.g. 12345), is here"); echo String($s)->replace(array('string', 'number'), function ($r) { return $r->changeCase(Str::ALL, Str::RANDOM); }); // Some sTrINg with NuMbEr(e.g. 12345), is here echo "\n"; Map(1, 4, 7, 9, 15)->clear(function ($value, $key) { if ($key == 0) { return true; // Элемент с ключем равным 0 оставляем независимо от значение } if ($value > 6 and $value < 13) { return true; // Оставить только значения между 6 и 13 } else { return false; // Все остальное убить } })->dump(); // Map (7, 9); // Метод сам определит - здесь лямбдда функция, или обычный параметр (например, строка, или объект String): echo String($s)->pregReplace("/([0-9]+)/", function ($m) { return "!$m[0]!"; // Some string with number(e.g. !12345!), is here }); echo "\n"; // Для более старых версий все-еще поддерживается старый синтаксис: echo String($s)->pregReplaceCallback("/([0-9]+)/", create_function('$m', 'return "!$m[0]!";' // Some string with number(e.g. !12345!), is here )); Ну и, при всей моей нелюбви к Руби из-за его ужасных beg; end; и т.п. тот метод, которым кичатся все Руби-прогеры : PHP: <?php Number(5)->times(function ($i) use ($n) { echo ($i->isEven()) ? "Even: " : "Odd : "; echo $n , "." , $i , ";\n"; }); Код (Text): Even: 5.0; Odd : 5.1; Even: 5.2; Odd : 5.3; Even: 5.4; Не так изящно, как в оригинале, конечно… Но, тем не менее, своя романтика в этом тоже есть. Тем более, обратите внимание - в Руби нельзя узнать, какой именно сейчас проход цикла, а тут можно ) Конечно, метод times врядли имеет право на существование, но я думаю, что это отличная демонстрация потенциала и намек на то, в какую сторону можно думать. На счет php 5.3. Думаю, уже через полгода он будет доступен на трети всех хостингов, а еще через годик он будет доминирующим. Стоит привыкать уже сейчас. Не всю ж жизнь php4 поддерживать - надо идти вперед.
Но, так как у меня теперь версия php 5.3.0, мне нужна помощь в проверке на совместимость с более старыми версиями. Хотя бы с 5.2.* Потому, кто еще не перешел, проверьте, пожалуйста, следующий код: PHP: <?php class Test { private $test; public function __construct () { $this->test = $this; } } $m = new Map (new Test); $m[] = $m; $m->dump('Recursion Test', 1); ?> Интересует, не уйдет ли dump в бесконечную рекурсию на старой версии php. При использовании php 5.3 мне выдало следующую ошибку: Код (Text): Deprecated: Call-time pass-by-reference has been deprecated Потому из строки Код (Text): $recurs = (in_array(&$object, $parents, true)) ? пришлость убрать амперсанд (&). У меня сейчас работает, но почему-то мне кажется, что я его тогда не зря поставил.
Это где? В официальном мане не нашел такого Код (Text): bool in_array ( mixed $needle , array $haystack [, bool $strict ] ) UPD: Понял. Как я могу описать в определении функции in_array передачу по ссылке, если это встроенная функция. Я знаю все недостатки использования ссылки. Я же говорю, что, скорее всего, я ее не зря туда поставил.
еще раз говорю. насколько я помню без & на пхп 5.2 возвращало фолс в то время, как с & возвращало тру. Что меня неприятно удивило и пришлось поставить, хотя я знаю все эти ньюансы. Может ли кто-либо просто проверить, а не рассказывать о теории, которую я прекрасно знаю?