• XSS.stack #1 – первый литературный журнал от юзеров форума

PHP Полезные классы и функции

sorax

HDD-drive
Пользователь
Регистрация
05.10.2018
Сообщения
29
Реакции
24
Друзья, в данной теме предлагаю делиться своими собственными полезными наработками на PHP.
Выкладывайте только личный код с небольшими объяснениями принципов работы.
 
Класс для генерации и проверки капчи

Часто в проектах требуется прикрепить капчу на различные формы. Предлагаю свой простой класс для работы с капчей, который позволяет сгенерировать проверочное изображение по заданным вами параметрам, а так же произвести проверку введенных данных.

PHP:
class Captcha
{
    private
        $length = 5,
        $width  = 200,
        $height = 36,
        $letters = "0123456789",
        $fontsize = 20,
        $font = "ПутьДоШрифта",
        $noise = true;

    public function __construct($params = null)
    {
        if(is_array($params))
            foreach($params as $key=>$value)
                if(isset($this->$key)) $this->key = $value;
    }

    private function RandWord()
    {
        $word = "";
        for($i = 0; $i < $this->length; $i++)
            $word .= $this->letters[rand(0, strlen($this->letters) - 1)];
        return $word;
    }

    public function Get()
    {
        header('Content-type: image/png');
        $word = $this->RandWord();
        $image = imagecreatetruecolor($this->width, $this->height);
        imagesavealpha($image, true);
        $background = imagecolorallocatealpha($image, 0, 0, 0, 127);
        imagefill($image, 0, 0, $background);
        if($this->noise) $this->AddNoise($image);
        for($i = 0; $i < $this->length; $i++)
        {
            $x = ($this->width - 20) / $this->length * $i + 10; $x = rand($x, $x + 4);
            $y = $this->height - (($this->height - $this->fontsize) / 2);
            $color = imagecolorallocate($image, rand(0, 130), rand(0, 130), rand(0, 130));
            $angle = rand(-25, 25);
            imagettftext($image, $this->fontsize, $angle, $x, $y, $color, $this->font, $word[$i]);
        }
        $_SESSION['captcha'] = $word;
        imagepng($image);
        imagedestroy($image);
    }

    private function AddNoise($image)
    {
        for($i=0; $i<1000; $i++) {
            $color = imagecolorallocate($image, rand(0, 100), rand(0, 100), rand(0, 100));
            imagesetpixel($image, rand(0, $this->width), rand(0, $this->height), $color);
        }
        for($i = 0; $i < rand(2, 5); $i++)
        {
            $color = imagecolorallocate($image, rand(0, 100), rand(0, 100), rand(0, 100));
            imageline($image, 0, rand(0, $this->height), $this->width, rand(0, $this->height), $color);
        }
    }

    public static function Verify($captcha)
    {
        if(!isset($_SESSION['captcha'])) return false;
        return (strtolower($captcha) == strtolower($_SESSION['captcha']));
    }
    
}

Вы можете указать свои параметры длины проверочного слова, ширины и высоты изображения, список символов, используемых для генерации, размер шрифта, путь до шрифта и наличие шума на изображении. Все эти параметры можно передать в конструкторе при создании экземпляра класса.

Небольшой пример использования:

PHP:
# Создаем экземпляр класса, можно указать свои параметры
$captcha = new Captcha(["font"=>"FONT.ttf", "width"=>150, "height"=>40]);

# В нужном месте вашего скрипта выводим само изображение
# Капча храниться в массиве $_SESSION
$captcha->Get();

# Статическим методом Verify можем проверить правильность введеных данных
Captcha::Verify("Введенные пользователем символы");
 
Все понятно из названия!

PHP:
function GetIP() {
    if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
        $ip = $_SERVER['HTTP_CLIENT_IP'];
    } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $i = $_SERVER['HTTP_X_FORWARDED_FOR'];
        $p = explode(",", $i);
        $ip = $p[0];         
    }
return filter_var($ip, FILTER_VALIDATE_IP) ? $ip :  $_SERVER['REMOTE_ADDR'];
}
 
Последнее редактирование:
Все понятно из названия!

Как минимум, нужно добавить:
PHP:
return filter_var($ip, FILTER_VALIDATE_IP) ? $ip :  $_SERVER['REMOTE_ADDR'];

и убрать блок else соответственно.
 
Описание

Класс ErrorHandlers содержит методы вызываемые во время возникновения ошибок и исключений. В нём реализованы: накопление ошибок и исключений, определение того внутри чего возникла ошибка или исключение, два режима сообщений - отладочный и пользовательский.


Константы

bool DEBUG переключает режимы сообщения(message).

Если DEBUG == true, то к началу сообщения добавляется место вызова тригера(trigger_error).
Например:
Код:
function() - Error message in function
    или
Class::method() - Error message in method

Если DEBUG == false, то сообщение без места вызова.
Например:
Код:
Error message in function
    или
Error message in method


Переменные

static array $errorsStack - служит накопителем ошибок и исключений возникающих при выполнении.
Имеет вид:
Код:
$errorsStack = [
    0 => [
        'type' => "notice", // [ notice | warning | error | exception ]
        'message' => "This string user must read",
        'file' => "index.php",
        'line' => 2,
    ],
    ...
    N => [
        'type' => "exception", // [ notice | warning | error | exception ]
        'message' => "Can't open connect to database",
        'file' => "init.php",
        'line' => 4,
    ],
];

static bool $displayErrorsOnly - Если true и включен output buffer, то self::$printErrors() обязан очистить буфер и отправить на вывод только ошибки. Если false и включён output buffer, то self::$printErrors() обязан вывести и буфер и ошибки.

static bool $collectErrors - Если true, то ошибки накапливаются в self::$errorsStack. Если false, то сообщение об ошибке отправляется с вызовом self::$printErrors(), который обязан его вывести в php://stderr.

static string $printErrors - Сюда помещаем название функции, которая отвечает за вывод ошибок. is_callable(ErrorHandlers::$printErrors) должно возвращать true.


Функции

static function errorHandler(int $code, string $message, string $file, int $line) - обработчик ошибок, подсоединяемый с помощью set_error_handler. Имеет два режима: если error_reporting() == E_ALL, то в стэк ошибок будут помещены все ошибки. Если error_reporting() != E_ALL && $code == E_USER_NOTICE, то в стэк ошибок будут отправлены только E_USER_NOTICE.

static function exceptionHandler($exception) - обработчик исключений, подсоединяемый с помощью set_exception_handler. Помещает в стэк исключения только если error_reporting() == E_ALL.

static function shutdownFunction() - обработчик критических ошибок, подсоединяемый с помощью register_shutdown_function. Помещает в стэк только ошибки от php и только если error_reporting() == E_ALL.


Оптимальные настройки

ErrorHandlers::$displayErrorsOnly = true; - нужно делать в сочетании с
ob_start();
ErrorHandlers::$collectErrors = true;


ErrorHandlers::$collectErrors = false; - нужно делать в cli и в сочетании с
ErrorHandlers::$displayErrorsOnly = false;


Примеры правильного использования с внешней функцией вывода ошибок

ErrorHandlers.php
Код:
<?php

if (!defined("DEBUG")) define("DEBUG", false);

class ErrorHandlers
{
    static $errorsStack = [];
    static $displayErrorsOnly = false;
    static $collectErrors = true;
    static $printErrors = "var_dump";

    static function errorHandler(int $code, string $message, string $file, int $line)
    {//{{{
        error_clear_last();

        $errors = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, E_STRICT, E_RECOVERABLE_ERROR];
        $warnings = [E_WARNING, E_CORE_WARNING, E_COMPILE_WARNING, E_USER_WARNING, E_DEPRECATED, E_USER_DEPRECATED];
        $notices = [E_NOTICE, E_USER_NOTICE];

        // Setting universal error type
        $type = 'unknown';
        if (in_array($code, $errors)) {
            $type = 'error';
        } elseif (in_array($code, $warnings)) {
            $type = 'warning';
        } elseif (in_array($code, $notices)) {
            $type = 'notice';
        }
       
        // Getting caller name where error triggered
        if (DEBUG) {
            $debug_backtrace = debug_backtrace();
           
            if (isset($debug_backtrace[2])) {
                $debug_backtrace = $debug_backtrace[2];
           
                if (isset($debug_backtrace['function'])) {
                    $function = $debug_backtrace['function'];
                    $message = "{$function}() - {$message}";
                }
               
                if (isset($debug_backtrace['class'])) {
                    $class = $debug_backtrace['class'];
                    $message = "{$class}::{$message}";
                }
            }
        }
       
        // Push to stack all errors messages or only user notice
        if (
            (error_reporting() != E_ALL && $code == E_USER_NOTICE)
            || error_reporting() == E_ALL
        ) {
            array_push(self::$errorsStack, [
                'type' => $type,
                'message' => $message,
                'file' => $file,
                'line' => $line,
            ]);
        }
       
        // Print error messages if collect them disabled
        if (!self::$collectErrors) {
            call_user_func(self::$printErrors, self::$errorsStack);
            self::$errorsStack = [];
        }
       
        return true;
    }//}}}

    static function exceptionHandler($exception)
    {//{{{
        $message  = $exception->getMessage();
       
        // Getting caller name where exception thrown
        if (DEBUG) {
            $debug_backtrace = $exception->getTrace();
           
            if (isset($debug_backtrace[0])) {
                $debug_backtrace = $debug_backtrace[0];
           
                if (isset($debug_backtrace['function'])) {
                    $function = $debug_backtrace['function'];
                    $message = "{$function}() - {$message}";
                }
               
                if (isset($debug_backtrace['class'])) {
                    $class = $debug_backtrace['class'];
                    $message = "{$class}::{$message}";
                }
            }
        }
       
        // Push exception message to stack if all errors level
        if (error_reporting() == E_ALL) {
            array_push(self::$errorsStack, [
                'type' => 'exception',
                'message' => $message,
                'file' => $exception->getFile(),
                'line' => $exception->getLine(),
            ]);
        }
       
        // Print exception messages if collect them disabled
        if (!self::$collectErrors) {
            call_user_func(self::$printErrors, self::$errorsStack);
            self::$errorsStack = [];
        }
       
        return true;
    }//}}}

    static function shutdownFunction()
    {//{{{
        $error = error_get_last();
        $errors = [E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING];
       
        $message = $error['message'];

        // Push to stack error if level all and error is php error
        if (
            error_reporting() == E_ALL
            && in_array($error['type'], $errors)
        ) {
            array_push(self::$errorsStack, [
                'type' => 'error',
                'message' => $message,
                'file' => $error['file'],
                'line' => $error['line'],
            ]);
        }
       
        // Outputting errors if present
        if (count(self::$errorsStack) > 0) {
            call_user_func(self::$printErrors, self::$errorsStack);
        }
   
       
   
        return true;
    }//}}}
   
}

// Connecting handlers
set_error_handler('ErrorHandlers::errorHandler');
set_exception_handler('ErrorHandlers::exceptionHandler');
register_shutdown_function('ErrorHandlers::shutdownFunction');

print_errors.php
Код:
<?php
if (!defined("DEBUG")) define("DEBUG", false);

function print_errors(array $errorsStack)
{//{{{
    if (ErrorHandlers::$displayErrorsOnly) {
        if (!empty(ob_get_status())) ob_clean();
    }
   
    $print_ob = function()
    {
        if (!empty(ob_get_status())) {
            ob_flush();
        }
        return true;
    };
    $prepend_ob = function(string $text) {
        if (!empty(ob_get_status())) {
            $ob = ob_get_contents();
            ob_clean();
        }
        echo $text;
        if (!empty(ob_get_status())) {
            echo $ob;
            ob_flush();
        }
    };
   
    if (PHP_SAPI == "cli") {
        $text = "";
        foreach ($errorsStack as $error) {
            if (DEBUG) {
                $text .=
                    "{$error['type']}".PHP_EOL.
                    "{$error['file']}:{$error['line']}".PHP_EOL.
                    "{$error['message']}".PHP_EOL;
            }
            else {
                $text .= "{$error['message']}".PHP_EOL;
            }
        }
        $print_ob();
        file_put_contents("php://stderr", $text, FILE_APPEND);
        return true;
    }

    if (isset($_SERVER['REQUEST_METHOD'])) {
        http_response_code(500);
       
        if ($_SERVER['REQUEST_METHOD'] == "GET") {
            $text = "";
            foreach ($errorsStack as $error) {
                if (DEBUG) {
                    $text .=
                        "<code>".
                        "<b>{$error['type']}</b><br>".
                        "{$error['file']}:{$error['line']}<br>".
                        "{$error['message']}<br>".
                        "</code>";
                }
                else {
                    $text .=
                        "<code>".
                        "{$error['message']}<br>".
                        "</code>";
                }
            }
           
            if (ErrorHandlers::$collectErrors) {
                $prepend_ob($text);
            }
            else {
                echo $text;
            }
           
            return true;
        }
       
        if ($_SERVER['REQUEST_METHOD'] == "POST") {
            $array = [];
            foreach ($errorsStack as $error) {
                if (DEBUG) {
                    $array[] = [
                        'type' => $error['type'],
                        'file' => $error['file'],
                        'line' => $error['line'],
                        'message' => $error['message'],
                    ];
                }
                else {
                    $array[] = [
                        'message' => $error['message'],
                    ];
                }
            }
            $text = json_encode($array, JSON_PRETTY_PRINT);
           
            if (ErrorHandlers::$collectErrors) {
                $prepend_ob($text);
            }
            else {
                echo $text;
            }
           
            return true;
        }
    }

    trigger_error("unexpected error", E_USER_WARNING);
    return false;
}//}}}

index.php
Код:
<?php
define("DEBUG", true);
error_reporting(E_ALL);

require_once __DIR__."/ErrorHandlers.php";
require_once __DIR__."/print_errors.php";

// Set print errors function
ErrorHandlers::$printErrors = "print_errors";

// Configuring cli
if (PHP_SAPI == "cli") {
    ErrorHandlers::$displayErrorsOnly = false;
    ErrorHandlers::$collectErrors = false;
}

// Configuring GET
if (
    isset($_SERVER['REQUEST_METHOD'])
    && $_SERVER['REQUEST_METHOD'] == "GET"
) {
    ob_start();
   
    ErrorHandlers::$displayErrorsOnly = false;
    ErrorHandlers::$collectErrors = true;
   
    echo "<html><body><pre>";
}

// Configuring POST
if (
    isset($_SERVER['REQUEST_METHOD'])
    && $_SERVER['REQUEST_METHOD'] == "POST"
) {
    ob_start();
   
    ErrorHandlers::$displayErrorsOnly = true;
    ErrorHandlers::$collectErrors = true;
}


function f()
{
    echo "\nBefore warning\n";
    trigger_error("Warning text", E_USER_WARNING);
    echo "After warning\n";
   
    return NULL;
}

class A
{
    function b()
    {
        echo "\nBefore error\n";
        trigger_error("Error text", E_USER_ERROR);
        echo "After error\n";
       
        return NULL;
    }
}

echo "Before notice\n";
trigger_error("Notice text", E_USER_NOTICE);
echo "After notice\n";

f();

$A = new A;
$A->b();

if (PHP_SAPI == "cli") {
    echo "\nBefore exception\n";
    throw new Exception('Exception text');
    echo "After exception\n";
}

if (
    isset($_SERVER['REQUEST_METHOD'])
    && $_SERVER['REQUEST_METHOD'] == "GET"
) {
    echo "</pre><form action='/' method='post'><input type='submit'/></form></body></html>";
}
exit(0);
 
Последнее редактирование:
Иногда бывает нужно найти среди кучи инклудов где конкретно определена функция. И если grep не доступен по той или иной причине, то помогает такой код:
PHP:
$reflFunc = new ReflectionFunction('function_name');
print $reflFunc->getFileName() . ':' . $reflFunc->getStartLine();
Также этот код помогает найти нужный файл когда он закрыт каким-нить ionCube - что дает возможность работать с конкретным файлом, а не пытаться вскрывать их все.
 
В продолжение по теме IP.
Обращение к API для определения информации по IP адресу

PHP:
function get_ip_info($ip){
    $ch = curl_init();
    $url="http://ip-api.com/json/{ip_address}";
    $url=preg_replace("/\{ip_address\}/i",$ip,$url);
    curl_setopt($ch, CURLOPT_URL, $url);     
    curl_setopt($ch, CURLINFO_HEADER_OUT, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $r = curl_exec($ch);
    curl_close($ch);
    return $r;
}

print_r(json_decode(get_ip_info('ANY_IP_HERE'),true));
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх