Програмен код на PHP

PHP примери за напреднали

  • return; return;
  • continue 2
  • catch(Excepton1|Exception2 $e)
  • isset($param1, $param2, $param3)
  • !empty(array_intersect(['firstname','lastname'], array_keys(array_filter($array))))
  • iterator_to_array(new \RecursiveIteratorIterator(new \RecursiveArrayIterator($array)))
  • preg_match("/(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})/", $dateString, $coordinates)

Това са функционалности на PHP, които не са описани в началните курсове или не се покриват в повечето уроци свързани с PHP. Исках да извадя някои от скорошните ми способи за подобрение на кода. Всяка една функционалност може да се напише и по друг начин, но с повече редове или по-бавни (както ги срещам най-често). Бих казал, че това са най-кратко написаните решения на даден проблем. Ако все пак някой знае по-добър ще се радвам да го науча.

return; return;

Добре де, return; return; не е всъщност нещо което работи – поне след първия return, но исках да напомня да внимавате за неизпълним код. По-лошото от код който трудно се поддържа е само код, които никога и не се използва. До преди време исках да покрия всичко което правя и да включа допълнителна функционалност – FUTURE PROOF. Но ако имаш 10 такива библиотеки и използваш само 10% от написаното, всъщност ще ти е много трудно да ги поддържаш за в бъдеще (изключения са ако правите Framework, CMS,.. някоя външна библиотека)., защото изискванията винаги се сменят и неизползвания код става все по-голям.

continue 2

Ето това е нещо сравнително просто, което мноого рядко може да ви се наложи, но докато не прочетете в документацията няма да знаете дали можете да го направите. Ако няколко вложени цикъла то единственият начин да прескочите външните е като подадете втори параметър на continue. Това важи и за break. Ето един пример:
for($i = 1; $i <= 3; $i++) {
for($h = 1; $h <= 3; $h++) {
if ($h == 2) continue;
echo $h . " ";
if ($i == 2) continue 2;
}
echo $i . "\n";
}

Така ще получите:

1 1
3 1
1 1 3
3 3

continue може да получи броя на цикли, които трябва да прескочи, като по-подразбиране си е 1.

 

catch(Excepton1|Exception2 $e)

Това е нещо ново – php 7.1+, но ако често използвате try..catch може да ви спести повторения на код. Тук хващаме грешка ако е инстанция на един от 2-та класа.

Може да провчетете документацията за да се запознаете с всички подробности –exceptions

isset($param1, $param2, $param3)

Знаехте ли, че isset може да поема повече от 1 параметър. Накрая връща true само когато всеки един от тях е зададен.

Друг вариант за използването му е да проверите дали елементи в масив съдържат нулеви стойности като isset(...$array)

Документация: isset

!empty(array_intersect(['firstname','lastname'], array_keys(array_filter($array))))

А с този скрипт проверявам дали някои от елементите на списъка са не-празни и се срещат в масив. Ако масива ви е ['firstname' => '', 'lastname' => 'Doe'] ще върне true, но ако е ['firstname' => '', 'middlename' => 'Name'] ще върне false. Удобно е когато изпращате някоя форма да проверите първо дали има не-празни стойности за желаните елементи.

Документация: array_intersect, array_keys, array_filter

iterator_to_array(new \RecursiveIteratorIterator(new \RecursiveArrayIterator($array)))

Обхождането на много-измерни списъци е трудно и винаги се прави с рекурсия, но по този начин с 1 ред код може да вземем плоска версия на масива. Ако искаме просто да обходим всички елементи то можем да извикаме само new \RecursiveIteratorIterator(new \RecursiveArrayIterator($array)) и да обходим неговите елементи, понеже може да загубим някои стойности ако имаме едни и същи ключове (Пример: ['key' => 'val', 'other' => ['key' => 'val2']], което ще доведе до ['key' => 'val2'].

Документация: iterator_to_array, RecursiveIteratorIterator, RecursiveArrayIterator

preg_match("/(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})/", $subject, $matches)

Много често виждам $matches[1]/$matches[0]/$matches[2], което е трудно за четене може да доведе до грешки в някои по-сложни случаи. А така, като подадете ?P<name>директно задавате ключ на параметъраname(а 0,1,2 са си все там, ако желаете да ги използвате).

Документация: preg_match

Надявам се, че вие също ползвате подобни средства, за да улесните живота си. Ако се сещате за нещо подобно, което използвате може да оставите коментар.

, , ,

Няма коментари

Codeception

Лесен и достъпен начин за тестване на вашият код. Предимно за PHP приложения, но не е задължително.

Ако използвате Codeception може да покриете по всякакъв начин вашия код с unit, functional или acceptance тестове.

Документацията им е добре написана и лесно можете да започнете с първите си тестове.

Само като пример (с Composer) бих дал няколко команди които ще създадът всичко необходимо за създаване и изпълнение на Codeceptio тестове:

composer require codeception/codeception --dev
vendor/bin/codecept bootstrap
codecept generate:cept acceptance First
vendor/bin/codecept run

Това ще ви добави библиотеката, чрез Composer.
След това ще създаде стартова версия с unit, functional и acceptance тестови конфигурации.
Генерираме тестов файл с формат Cept за acceptance suit и име First.
Накрая пускаме проверка за всички тестове, които нямат никакви проверки, но все пак всички основни файлове са създадени и може да започнете доректно с покриването на вашия код.

След лекцията ми на RuseConf 2017, реших да дам малко повече публичност на начините за тестове. С тази публикация започвам серия от най-добри практики при писане не стандартни тестове.

Codeception има примерни тестове за много видове проекти, както и за фреймуърковете, които поддържат – Zend, Yii, Phalcon, Doctrene, Laravel, Simpfony, Silex..
Избрах да направя разширена версия на тестовете им на Laravel, за да покажа добри практики за по-напреднали тестове. Можете да я намерите в Github: https://github.com/ibpavlov/codeception-laravel5-advanced

, , , , ,

Няма коментари

JSON_NUMERIC_CHECK round-fix

Ако искате автоматично да преобразувате низове в числа при json_encode тогава един от начините е с JSON_NUMERIC_CHECK.

Но при него има доста често срещани проблеми като изрязването на телефоните и големите числа.

Това се отнася най-вече за запазени в някакъв обект, база от данни, или други които са пресметнати и са представени в консистентен формат. Например: при запазване на числата +123, 0123, 123 винаги ще се запазят като 123. Когато взимаме тези данни ние знаем, че подадените ни данни ще са вече изчислени, обработени и +123 не може да срещнем ако числото наистина не е низ от символи.

Ето една реализация на същата функционалност както при JSON_NUMERIC_CHECK, която не създава такива проблеми. Числата се заменят само ако не са толкова големи и ако не започват с „+“ или „0“ (т.е. са изчислени числа).


Опростената версия на кода:

function stringToNumeric(&$element)
{
    if(is_string($element) && is_numeric($element)) {
        //Check if normal int or float and change format
        if (ctype_digit($element) && strval(intval($element)) === $element) {
            $element = intval($element);
        } elseif(strval(doubleval($element)) === $element) {
            $element = doubleval($element);
        }
    }
}

function arrayStringsToNumeric(&$array) 
{
    array_walk_recursive($array, "stringToNumeric");
}

function jsonNumericEncode($data, $options = 0, $depth = 512)
{
    //if Has Numberic check and is_array do proper check
    if(($options & JSON_NUMERIC_CHECK) == JSON_NUMERIC_CHECK && is_array($data)) {
        //Remove numeric check
        $options = $options ^ JSON_NUMERIC_CHECK;
        //Make proper type
        array_walk_recursive($data, "stringToNumeric");
    }
    json_encode($data, $options, $depth);
}
jsonNumericEncode

Функцията замества използването на JSON_NUMERIC_CHECK, но може и да се ползва директно като премахнете тази проверка.

function prepareJsonData($data)
{
    //if Has Numberic check and is_array do proper check
    if((is_array($data) && !empty($data)) {
        //Make proper type
        array_walk_recursive($data, "stringToNumeric");
    }
}
$data = prepareJsonData($data);
json_encode($data);

Така накрая в данните може да получите

{
    "phone": "+525526262",
    "number: 12512521, 
    "phone2": "0025542424252",
    "money": 22.23
}

вместо

{
    "phone": 525526262,
    "number: 12512521, 
    "phone2": 25542424252,
    "money": 22.23
}

Има и друго решение, което според мен не е толкова ефективно. Можете да продължите с използването на JSON_NUMERIC_CHECK, но да промените малко данните за да се запазят все пак като низ от символи:

function prepareJsonWithNumberCheck(&$data) {
array_walk_recursive($data, function(&$element) {
    //Check if big int or number and add space
    $same = strval(intval($element)) == $element;
    if (ctype_digit($element) && !$same) {
        $element = "" . $element . " ";
    } elseif(!$same && !preg_match('/^(\+|0)[0-9]+$/', $element)) {
        $element = "" . $element . " ";
    }
});
}

, , ,

Няма коментари

Активна навигация

Ако имате статична или динамична навигация, много пъти при изкарването и в нов файл трябва да се предвиди как ще се проверява дали линка трябва да е активен за текущата страница.

Вместо да добавям някакъв сложен код към навигацията съм разработил проста функция, която просто трябва да се извика с името на линка.


Простата версия на кода:

function active ($find, $exact = FALSE)
{
	if ( $exact ) {
		$server = explode('/', $_SERVER['REQUEST_URI']);
		$uri = $server[count($server) - 1];
		if ( strcmp($uri, $find) == 0 ) {
			return 'class="active"';
		}
	} else if ( strpos($_SERVER['REQUEST_URI'], $find) !== FALSE ) {
		return 'class="active"';
	}

    return "";
}

Сложната, която работи с масив:

function active ($find, $exact = FALSE)
{
    if(is_array($find)) {
        $server = explode('/', $_SERVER['REQUEST_URI']);
        if(!empty($find))
            foreach($find as $element) {
                if ( $exact ) {
                    $uri = $server[count($server) - 1];
                    if ( strcmp($uri, $element) == 0 ) {
                        return 'class="active"';
                    }
                } else if ( strpos($_SERVER['REQUEST_URI'], $element) !== FALSE ) {
                    return 'class="active"';
                }
            }
    } else {
        if ( $exact ) {
            $server = explode('/', $_SERVER['REQUEST_URI']);
            $uri = $server[count($server) - 1];
            if ( strcmp($uri, $find) == 0 ) {
                return 'class="active"';
            }
        } else if ( strpos($_SERVER['REQUEST_URI'], $find) !== FALSE ) {
            return 'class="active"';
        }
    }

    return "";
}

Ако при линковете има други класове, може да се замени ‘class=“active“‘ с „active“.

Пример за използване:


Няма коментари

FPDF полезни функции

FPDF е клас на php за изработка на pdf документи. Чрез него може да направите точно каквото пожелаете.
За разлика от конвертирането на HTML към PDF, което не може да се контролира точно как ще се покаже, чрез FPDF имате пълен контрол.

Пълния контрол е свързан с много функции, по-дълго програмиране, но по-добър ефект.

Ето и няколко добри помощни функции, както и трикове за по-лесна употреба и контрол:

Задаване на разстояние между буквите (letter spacing):

function SetCharSpacing( $s_w , $s_l = 0) {
	$this->s_w = $s_w;
	$this->s_l = $s_l;
	$this->_out(
		'BT '.
		sprintf('%.3f Tc ', $this->s_w * $this->k).
		sprintf('%.3f Tw ', $this->s_l * $this->k).
		'ET '
	);
}

Изместване на текущата X, или Y позиция (без текст, без нов ред).

function Spacer($space) {
   $this->SetX($this->x + $space);
}

function SpacerY($space) {
    $this->y += $space;
}

Взимане дължината на реда:

function GetLineWidth() {
    return $this->w - $this->rMargin - $this->x;
}

Текст с булет за листа:

function SimpleListItem($w, $h, $txt, $border=0, $align='J', $fill=false, $listChar = '•') {
   $this->Spacer(6.4);
    $fs = $this->FontSizePt;
    $this->SetFont($this->FontFamily, '', 10);
    $this->Cell(6.4,3.5,$listChar);
    $this->SetFont($this->FontFamily, '', $fs);
    $this->MultiCell($w,$h,$txt,$border,$align,$fill);
}


Чекбокс, който се изразява в квадратче, което може да има тикче. Подобно на HTML chechbox, което се управлява само от една булева променлива

function Checkbox($checked = true) {
    $this->Rect($this->x,$this->y+0.4,4.1,3.8);
    if($checked) {
        $file = "images/checked.png";
        $w=3.5;
        $ax = $this->x+0.2;
        $ay = $this->y+0.6;
        $this->Image($file, $ax, $ay, $w, NULL, $type, $link);
    }
    $this->SetX($this->x + 4.1);
}

Има и други функции и класове, които са доста полезни:
Таблица, http://www.vonderborn.com/extended-tables-with-fpdf.php
HTML код, http://www.fpdf.de/downloads/addons/41/

Няма коментари

  1. Няма коментари.
(will not be published)