Dawno tu nie pisałem… No cóż, (bardzo) dużo pracy, życie osobiste i takie tam – to odstrasza od blogowania ;) Jeśli już, to od czasu do czasu coś publikuję jeśli chodzi o zdjęcia, a sprawy “kodowe” to raczej “robię” niż o nich piszę :)

Jako że od kilka dni jestem na L4, to się ostro opierdzielam, ale dopiero dziś jestem w stanie podjąć jakikolwiek intelektualny wysiłek… No to popatrzyłem sobie m.in. na GoldenLine. Dziś na forum PHPowym ktoś poruszył dość banalny problem, ktoś inny podrzucił rozwiązanie (preg_replace_callback (), a ja zacząłem się zastanawiać czemu wszyscy w PHP tak napierają na regexpy (rozumiem w Perlu, ale PHP?). Jak zacząłem się zastanawiać jak to zrobić bez regexpów, to zacząłem się bawić w zakodowanie tego ;)

Najpierw zrobiłem dość banalną wersję, przy użyciu strlen (), strpos () i substr (), i chciałem ją podać jako wersję hardcore ;) Wtedy pomyślałem sobie, że prawdziwa wersja hardcore, to byłaby bez użycia tychże funkcji… najprostsza postać tego to zastąpienie tychże funkcji swoimi ;) I o ile substr () i strpos () są dość banalne, o tyle dla mnie prawdziwym hardcorem jest strlen () ;)

Oczywiście nie są to najbardziej optymalne wydajne rozwiązania (a jeśli są to tylko przypadkiem), bo jeśli chodzi o optymalizację, to trzeba by używać wersji natywnych to trzeba by się najpierw zastanowić i sprawdzić czy jest co optymalizować… :) A całość warto potraktować stricte jako zabawę, którą dla mnie było pisanie tego :)

W całym “problemie” chodziło o zastąpienie w tekście wystąpień “[userId]ID[/userId]” tym, co zostanie wyplute przez dodatkową funkcję której podajemy zawartość tego co jest między znacznikami. Użycie preg_replace_callback () jest chyba najbardziej intuicyjnym rozwiązaniem, ale nie jedynym… :) Poniżej obydwie wersje: hardcore i bardziej hardcore – enjoy ;)

hardcore

function replace_user_id ($str, $fn) {
    $tags = array ('start' => '[userId]', 'end' => '[/userId]');
    $tag_len = array (
        'start' => strlen ($tags['start']),
        'end'   => strlen ($tags['end'])
    );
    $offset = $pos_end = 0;
    $ret    = '';
    $strlen = strlen ($str);

    while ($pos_end < $strlen) {
        if (
            ($pos_start = strpos ($str, $tags['start'], $pos_end)) !== false &&
            ($pos_end   = strpos ($str, $tags['end'], $pos_start)) !== false
        ) {
            $ret .= substr ($str, $offset, $pos_start - $offset);
            $pos_start += $tag_len['start'];

            $ret .= $fn (substr ($str, $pos_start, $pos_end - $pos_start));

            $pos_end += $tag_len['end'];
            $offset = $pos_end;
        }
        else {
            break;
        }
    }

    return $ret;
}

bardziej hardcore

class __mstrlen__ErrH {
    private static $error = false;
    private function __construct () {}
    static public function errh ($errno, $errstr, $errfile = '', $errline = 0, $errctx = array ()) {
        if ($errno != E_NOTICE) {
            return false;
        }

        $expected = 'Uninitialized string offset';
        for ($i=0; $i<26; ++$i) {
            if ($expected[$i] != $errstr[$i]) {
                return false;
            }
        }

        self::$error = true;
    }
    public static function isError () {
        return self::$error;
    }
    public static function zero () {
        self::$error = false;
    }
}

function mstrlen ($str) {
    $str = (string) $str;

    $len = -1;
    __mstrlen__ErrH::zero ();
    set_error_handler (array ('__mstrlen__ErrH', 'errh'), E_NOTICE);
    while (__mstrlen__ErrH::isError () === false) {
        ++$len;
        $q = $str[$len];
    }

    restore_error_handler ();
    return $len < 0 ? 0 : $len;
}

function mstrpos ($str, $seek, $offset=0) {
    if (!is_int ($offset) || $offset < 0) {
        $offset = 0;
    }

    $str_len = mstrlen ($str);
    if ($offset > $str_len) {
        return false;
    }

    $seek_len = mstrlen ($seek);
    for ($i=$offset; $i < $str_len; ++$i) {
        for ($j=0; $j < $seek_len; ++$j) {
            if ($str[$i + $j] != $seek[$j]) {
                break;
            }
        }

        if ($j == $seek_len) {
            return $i;
        }
    }
    return false;
}

function msubstr ($str, $start, $length = null) {
    $str_len = mstrlen ($str);
    if (!is_int ($start)) {
        trigger_error ('Incorrect offset');
    }
    else if ($start >= $str_len) {
        return false;
    }
    else if ($start < 0) {
        $start = $str_len + $start;
    }

    if (!is_int ($length)) {
        $length = $str_len - $start;
    }
    else if ($length < 0) {
        $length = $str_len + $length - $start;
    }
    else if ($length + $start > $str_len) {
        $length = $str_len - $start;
    }

    $ret = '';
    for ($i=0; $i < $length; ++$i) {
        $ret .= $str[$i + $start];
    }

    return $ret;
}

function replace_user_id2 ($str, $fn) {
    $tags = array ('start' => '[userId]', 'end' => '[/userId]');
    $tag_len = array (
        'start' => mstrlen ($tags['start']),
        'end'   => mstrlen ($tags['end'])
    );
    $offset = $pos_end = 0;
    $ret    = '';
    $strlen = mstrlen ($str);

    while ($pos_end < $strlen) {
        if (
            ($pos_start = mstrpos ($str, $tags['start'], $pos_end)) !== false &&
            ($pos_end   = mstrpos ($str, $tags['end'], $pos_start)) !== false
        ) {
            $ret .= msubstr ($str, $offset, $pos_start - $offset);
            $pos_start += $tag_len['start'];

            $ret .= $fn (msubstr ($str, $pos_start, $pos_end - $pos_start));

            $pos_end += $tag_len['end'];
            $offset = $pos_end;
        }
        else {
            break;
        }
    }

    return $ret;
}

Liczba komentarzy: 11

  1. 1 Był piątek, 17 Kwiecień 2009 roku gdy o godzinie 18:58 przyszedł scanner i stwierdził:

    Nudzisz się na tym L4 jak widzę, strasznie się nudzisz…

  2. 2 Był piątek, 17 Kwiecień 2009 roku gdy o godzinie 19:01 przyszedł MySZ i stwierdził:

    @scanner: e, potrzebowałem po 4 dniach męczarni w końcu jakiejś rozrywki intelektualnej, a to był tylko taki ładny wyzwalacz… ;)

  3. 3 Był piątek, 17 Kwiecień 2009 roku gdy o godzinie 20:43 przyszedł Piotr Ożarowski i stwierdził:

    Kolega widzę ma zadatki na polityka, oni też szukają rozwiązań “bardziej optymalnych” ;-P

    (hint: nie ma lepszych rozwiązań niż te optymalne)

  4. 4 Była sobota, 18 Kwiecień 2009 roku gdy o godzinie 08:12 przyszedł MySZ i stwierdził:

    Kolega widzę ma zadatki na polityka

    No teraz to się powinienem obrazić i wymoderować komentarz… ;)

    “bardziej optymalnych”

    Oj tam, chodziło o wydajność, pilnowanie się żeby dane rozwiązanie było optymalne wydajnościowo to od jakiegoś czasu mam we krwi… ;)

  5. 5 Była sobota, 18 Kwiecień 2009 roku gdy o godzinie 14:06 przyszedł Piotr Ożarowski i stwierdził:

    Oj tam, chodziło o wydajność

    No polityk jak nic, nawet się tłumaczy skrótami myślowymi ;-)

    A tak poważnie, to usuń słówko “najbardziej” sprzed “optymalne rozwiązania” bo to strasznie kole w oczy – chyba zbyt wiele razy musiałem udowadniać, że coś jest optymalne[0], aby teraz nie zwracać na to uwagi.

    [0] bez dowodu nie można twierdzić, że coś jest optymalne!

  6. 6 Była sobota, 18 Kwiecień 2009 roku gdy o godzinie 15:09 przyszedł Michał Bachowski i stwierdził:

    Rozumiem, że wersja “bardziej hardcore” to faktycznie zabawa, bo wywoływanie błędów raczej niewiele ma wspólnego z optymalnością :>

    A czy wykorzystanie “isset” też było zabronione? Bo taki “mstrlen” można rozwiązać dużo prościej wykorzystując tę funkcję :>

    Pozdrawiam

  7. 7 Była sobota, 18 Kwiecień 2009 roku gdy o godzinie 15:48 przyszedł MySZ i stwierdził:

    @Piotr Ożarowski: Poprawilem ten fragment, tak lepiej? ;)

    @Michał Bachowski: Obydwie wersje to zabawa. A co do isset () – to by było za proste ;) Choć muszę przyznać, że inspiracją dla mojej wersji było C – ale PHP nie oznacza końca stringu poprzez \x{0} ;)

  8. 8 Była sobota, 25 Kwiecień 2009 roku gdy o godzinie 18:33 przyszedł dr_bonzo i stwierdził:

    Chory jestes/byles :D

  9. 9 Była niedziela, 26 Kwiecień 2009 roku gdy o godzinie 02:26 przyszedł MySZ i stwierdził:

    @ dr_bonzo: no nie da się ukryć ;)

  10. 10 Był poniedziałek, 27 Kwiecień 2009 roku gdy o godzinie 12:56 przyszedł Zyx i stwierdził:

    Tu z kolei przegięcie w drugą stronę. Wyrażenia regularne są wygodnym narzędziem właśnie po to, by nie trzeba było klepać mega-parserów za każdym razem, gdy chcemy coś sprawdzić, czy zamienić. Jednak w wielu przypadkach nie zastąpią one kompletnego algorytmu, albo taka wersja będzie pozbawiona pewnych cech (np. przy parsowaniu XML-a czystymi wyrażeniami regularnymi wyłapie nam prawidłowe dane, ale nie zgłosi nieprawidłowych jako błąd). Dla mnie wyrażenia regularne są tylko jednym z narzędzi i bardziej staram się pisać coś takiego, by mi produkował pewne wyjście, które następnie odpowiednio obrabiam już czystym PHP, by móc wykorzystać normalną algorytmikę tam, gdzie to konieczne. Niczym dla mnie nowym jest też kodowanie pewnych partii w PHP, choć teoretycznie wyrażenia regularne też mogłyby sobie poradzić. Złoty środek, panowie, złoty środek.

  11. 11 Była niedziela, 10 Maj 2009 roku gdy o godzinie 11:20 przyszedł khung- znaczy ciemnosc :) i stwierdził:

    hm:) funkcje sa piekne owszem ale procek i tak przetwarza w jednym kodzie 010101010101011110101 no coz bledy kompilatorow tez przeciez sie zdazaja jestem pelen podziwu ale zgodzicie sie ze mna że prawdziwy hardcore to wlasnie :
    0100101101111010001101110110…… :) to oczywiscie belkot ale co jesli stanie sie faktem ?

A Ty? Co o tym myślisz?

Możesz używać w komentarzach następujących znaczników:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Sblam! Antyspam