RestAPI для веб-приложения на PHP или познаем дзен в чистоте

В нынешней разработке все стремятся к чистоте. Чистый код и прозрачный для любого Джуниора паттерн – безусловно залог успешного долгоиграющего проекта, который еще не скоро соберутся переписывать.

В данной статье расскажу, как в течении нескольких лет я пришел к, в моем видении, идеальному решению для реализации Restfull API сервиса на PHP. Конечно, я в курсе, что существует бесчисленное множество фреймворков, которые позволяют за пару минут развернуть своё API. Но, меня всегда одолевали сомнения на их счет. Лично я – никогда не любил использовать чужой код. Сначала: из-за того, что не было уверенности 100% понимания всех происходящих процессов. Позднее: из за сомнений в том, что данный фреймворк – лучшее, что может быть написано для моего проекта.

Если Вы – ярый сторонник фреймворков и не понимаете мой выбор: нашел статью на хабре затрагивающую эту тему.

Итак, если Вы еще не определились какой подход использовать в реализации RestAPI для Вашего сервиса или просто хотите сравнить свой подход с моим – давайте смотреть код!

Перед тем как брать подход к себе на вооружение учтите: код был применен еще в далеком 12 году и написан по старым канонам. Содержит в себе массу шероховатостей. Ни в коем случае не стоит переносить содержимое классов в свои проекты. Польза приведенного ниже кода — максимум показать идею!

После освоения принципов переданных в статье обязательно посмотрите ссылки ниже:
www.php-fig.org/psr/psr-2
getcomposer.org


Также, по поводу вложенности условий, ценный совет дал комментатор alekciy habrahabr.ru/post/280121/#comment_8821069

Безусловно, перед началом Вам стоит настроить Ваш веб-сервер, что-бы все запросы летели в index.php, если Вы не знаете, как это сделать – под спойлером привожу пример настроек для Nginx.
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}

location /css/ {}
location / {
rewrite ^ /index.php last;
}


Начнем, конечно, с index.php:
В котором, как ни странно для MVC паттерна, практически ничего нет.

Для вызова API нам потребуется всего несколько строк:

$action = explode('/', $_SERVER['REQUEST_URI']);
if(!isset($action[1]))
    $page = NULL;
else
    $page = $action[1];

if($page == 'api') {
    include_once 'api/main/api.class.php';
    new api;
} else {

   //тут можете подгрузить свой клиентский код заинклюдив, например, index.html

}


Таким образом, теперь, любые обращения на URL sitename.com/api/что_то_там_еще будут способствовать только вызову класса API и ничего лишнего.

Далее, для понимания всей картины, давайте окинем взглядом архитектуру будущего приложения:
image

Скриншот иерархии предоставлен прямо с боевого проекта, а в статье мы затронем лишь папку api и файл index.php.

Файлы api по порядку:


api.class.php
У нас перед глазами основной файл, в котором кроется вся суть моего подхода: базовый класс играет роль роутера – он будет определять обращение к конкретному методу API, подготавливать все данные и осуществлять вызов названного метода.

include_once 'model.class.php';
include_once 'view.class.php';
include_once 'factory.class.php';

class api {
    private $db;
    private $view;
    private $factory;
    private $args = array();
    private $userID;

    function __construct() {

        $this->db = new db();
        $this->view = new view();
        $this->factory = new factory();
        $url = parse_url($_SERVER['REQUEST_URI']);
        $action = explode('/', $url['path']);
        $action = end($action);

        if (!empty($action)) {

            if (!empty($_POST) || (substr($action, 0, 4) == 'get_')) {

                if (file_exists('api/methods/' . $action . '/controller.php')) {

                    if (file_exists('api/methods/' . $action . '/parameters.inc.php')) {

                        $parameters = array();
                        $missing_parameters = array();
                        $wrong_types = array();

                        include_once('api/methods/' . $action . '/parameters.inc.php');

                        foreach ($parameters as $param => $value ) {

                            if (!empty($_POST[$param])) {
                                if(factory::check_parameter_type($_POST[$param], $value[1]))
                                    $this->args[$param] = factory::sanitize(factory::set_parameter_type($_POST[$param], $value[1])); //sanitize arguments from request body and assign argument
                                else
                                    $wrong_types[] = $param;

                            } elseif ($value[0] == 1)
                                $missing_parameters[] = $param;

                            else
                                $this->args[$param] = NULL;

                        }

                        if (empty($missing_parameters)) {
                            if (empty($wrong_types)) {

                                try {

                                    call_user_func_array(array($this, $action), $this->args); //request api method

                                } catch (ErrorException $e) {

                                    view::error($_POST, 503);

                                }

                            } else
                                view::error("Incorrect data type for: " . implode(', ', $wrong_types), 204);
                        } else
                            view::error("Missing parameters: " . implode(', ', $missing_parameters), 204);
                    } else
                        view::error("Method in developing.", 503);
                } else
                    view::error("The method '" . $action . "' does not exist.", 204);
            } else
                view::error("No params received.", 204);
        } else
            view::error("Method was not received.", 204);
    }

    public function __call($method, $args) { //create new api method from called arguments
        @include_once('api/methods/' . $method . '/controller.php');
        return true;
    }
}


Сам код довольно тривиален – инициализация, вылавливание из URL названия метода… Интересна лишь часть, где происходит выборка объектов из массива POST и преобразуется в аргументы функции которая, уже в дальнейшем, будет играть роль уникального метода.

factory.class.php
В данном файле я коплю функции, которые больше относятся к серверной стороне. Допустим тут можно сжать картинку, засунуть функцию для санитайза или подключить целую библиотеку.

class factory {
   public static function check_parameter_type($var, $type) {
        switch($type){
            case 'string':
                return true;
                break;
            case 'boolean':
                if(($var === 'true') || ($var === true))
                    return true;
                elseif(($var === 'false') || ($var === false))
                    return true;
                else
                    return false;
                break;
            case 'integer':
                return is_numeric($var) ? true : false;
                break;
            case 'smallint':
                return (is_numeric($var) && (strlen($var) == 1)) ? true : false;
                break;
            default:
                return false;
        }
    }

    public static function set_parameter_type($var, $type) {
        switch($type){
            case 'string':
                return $var;
                break;
            case 'boolean':
                if(($var === 'true') || ($var === true))
                    return 1;
                elseif(($var === 'false') || ($var === false))
                    return 0;
                else
                    return false;
                break;
            case 'integer':
                return $var;
                break;
            case 'smallint':
                return $var;
                break;
            default:
                return false;
        }
    }

}

Пример использования Вы уже видели в api.class.php
$this->args[$param] = factory::sanitize(factory::set_parameter_type($_POST[$param], $value[1]));


Следующий файл отвечает за работу с БД проекта
model.class.php
class db {
    public $current;
    private $_db;

    function __construct() {
        try {
            include 'db.config.php';
            $this->_db->exec('SET NAMES utf8mb4');
        } catch (PDOException $e) {

        }
    }

    function __destruct()
    {
        $this->_db = NULL;
    }

    protected function catch_db_error($query) {


            $dbh = $this->_db->query($query);

        if (!$dbh) {
            print $query;
            die(json_encode(array("Error" => "Syntax error.")));
        }
        return $dbh;
    }

    public function orm($sql, $array, $type){

        $this->_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

        try {
            $query = $this->_db->prepare($sql.';', array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
            $query->execute($array);
        } catch (Exception $e) {
            echo $e->getMessage();
            die();
        }

        switch($type) {
            case 'select_one':
                return $query->fetch(PDO::FETCH_ASSOC);
                break;
            case 'select':
                $query = $query->fetchAll(PDO::FETCH_ASSOC);
                if(is_array($query))
                    return $query;
                else
                    return array();
                break;
            case 'insert':
                return $this->_db->lastInsertId();
                break;
            case 'replace':
                return $this->_db->lastInsertId();
                break;
            case 'delete':
                return true;
                break;
            case 'update':
                return true;
                break;
            default:
                return true;
        }
    }

Также, при необходимости, сюда вписываются методы работы с БД, которые часто повторяются в проекте. Например, проверка пользователя, получение общих данных о нем и так далее.

view.class.php
View есть view и отвечает он за ответ API. Ответ, который, увидит запросивший в окне браузера.
class view
{
    static function encode($array) {

        array_walk_recursive($array, function (&$value) {
            if (!empty($value))
                $value = is_numeric($value) ? intval($value) : $value;
            else {
                if (($value === '') or ($value === NULL))
                    $value = NULL;
                elseif (($value === '0') or ($value === 0))
                    $value = 0;
            }
        });

        print json_encode($array);
    }

    static function error($text,$code = 400)
    {
        $result = array(
            'error' => $text,
            'status' => $code
        );

        return print json_encode($result);
    }

    static function state($state)
    {
        print $state == true ? json_encode(array('Status' => 'Successful.')) : json_encode(array('Status' => 'Invalid token.'));
    }

    static function status($text,$code) {
        $result = array(
            'message' => $text,
            'status' => $code
        );
        return print json_encode($result);
    }
}

В классе собраны методы для оформления сообщений и описание вывода словарей на JSON.

Итак, мы добрались до самого интересного – как же устроены методы в проекте?

Рассмотрим, на примере метода для входа
image

И снова – смотрим файлы по порядку.

Описание метода начинается с файла parameters.inc.php – мечта параноика.
Позволит вам почувствовать себя крутым программистом который пишет на серьезном языке со статической типизацией. К тому же вы будете точно знать, какой формат данных будет передан в метод.

$parameters = array(
    'login' => array(1, 'string'),
    'password' => array(1, 'string')
);

Все что тут храниться – массив входных аргументов для метода. 0 — не обязательный, 1 — обязательный и далее – тип переменной. Проверку на соответствие типу можете описать в factory.class.php. Если что-то пойдет не так вы увидите одну из ошибок, которые мы описали в api.class.php.

Сердце любого метода – контроллер.
controller.php

require('model.php'); $model = new model(); // load database methods

const SALT = 'ВаЩеОченьКруТаяСоЛь';

if((!empty($this->args['login'])) and (!empty($this->args['password']))) {

    if($userID = $model->login($this->args['login'], md5(crypt($this->args['password'],SALT)))) {
        @session_start();
        $_SESSION['userID'] = $userID;

        view::encode(array(
                "userID" => $userID
            )
        );

    } else
        view::error("Wrong login or password.", 200);
} else {
    view::error("Fill all fields.", 200);
}

Тут описана логика, обращение к импровизированной ORM, а также, вызывается необходимый метод из класса view для вывода ответа.

Куда же мы без базы
model.php

class model extends db {
    public function login($phone,$password) {

        $query = $this->orm('SELECT `UserID` FROM `UserPrivate` WHERE `Password` = :password AND `Phone` = :phone', array(
            ':phone' => $phone,
            ':password' => $password
        ), 'select_one');

        return $this->return_field($query, 'UserID');
    }
}

В папке метода файл model.php позволяет описывать дополнительные фуникции для работы с БД, которые, будут доступны только в текущем методе.

Таким образом – обращение к sitename.com/api/login вызовет описанный выше метод.

На этом описание моего Restfull API сервиса подходит к своему логическому завершению. Заранее прошу прощения за кучу ошибок, опечатки и форматирование.
Ads
AdBlock has stolen the banner, but banners are not teeth — they will be back

More

Comments 94

    +6
    http://www.php-fig.org/psr/psr-2/
    https://getcomposer.org/ — вот это примите как стандарт, 2016 год на дворе
    Ну и все это безобразие быстро и просто устанавливается из сторонних компонентов. Не любите фреймворки — библиотек для всего что в голову ударит вагон и маленькая тележка, и все протестированые и четко делают то, зачем они созданы
      0
      Приму на вооружение – спасибо!
      –6
      Воспользуюсь местом повыше дабы ответить на все нижеследующие комментарии разом.
      прозрачный для любого Джуниора паттерн

      Скажите, сколько джуниоров способны моментально интерпретировать современные лучшие практики в рабочий не забагованный проект?
      Безусловно, все в этом мире стоит улучшать. Однако, мой код позволяет любому начинающему программисту, за минимальное время, развернуть свое API для мобильного или веб-приложения. Притом, он будет наглядно понимать что и как в нем работает.
      Да и скажите — сколько будут стоить услуги человека, который знаком с этими лучшими практиками не по наслышке.
      В итоге — очень полезный для малого бизнеса инструмент позволяющий проверить гипотезу в течении суток.
      Я сам не люблю компромиссы, однако, особенно в кризис, я думаю — это уместно.
        0
        Однако мой код позволяет любому начинающему программисту, за минимальное время, развернуть свое API для мобильного или веб-приложения

        Хорошо, но почему не используете пространства имен, psr? Уже 2016 год. Если бы дело происходило до появления всего этого — то да, было бы познавательно. А psr приучит писать новичков понятный для остальных код. Ладно, пусть он один работает, даже так — через полгода глянет и ужаснется всему этому.
        Скажите, сколько джуниоров способны моментально интерпретировать современные лучшие практики в рабочий не забагованный проект?

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

        Дешевле, чем выйдет исправлять баги после проекта, реализованного новичком. Да, обучаться нужно, но чтоб подобное не попадало в боевой проект нужен code review.
        Я сам не люблю компромиссы, однако, особенно в кризис, я думаю — это уместно.

        Скупой платит дважды — отдав такое новичку, придется после доплачивать за исправление возможных багов. Или когда настанет момент расширить немного функционал — стоимость возрастет, и с ростом проекта кто-то глянет, плюнет и напишет с нуля — выйдет быстрее, чем поддерживать подобное.
        А если мыслить ближе к реальности — нечего джуниора к проектированию API подпускать, если только что-то очень-очень простое дать.
          –4
          Вот вот я к тому и говорю, дело в том, что в такой концепции джуниору будет очень трудно делать ошибки.

          Большинство джуниоров не знают как разделять логику. Благодаря моему подходу для них это станет полезной привычкой — а уже потом. Никто ведь не заставляет до сканчяния веков писать подобный код. Одни только неймспейсы давно пора применять.

          По поводу psr — согласен. Когда писал это в далеком бородатом году знал лишь о camelCase мой косяк.
          +2
          в течении пары часов можно прочитать статью "Пишем RestAPI на Slim/Silex/Yii2/Phalcon" итд, в которой вам покажут, что апи поднять можно в течении получаса, предложат хорошое структурирование кода и кодстайл по ПСР, а в конце дадут ссылочку на репозиторий на гитхабе с готовым кодом, чтобы вы тут же развернулись локально.
            –3
            В каждом фреймворке есть свои подводные камни. Сильные и слабые стороны. Мы говорим о джуниорах которые не смогут сходу этого понять и в дальнейшем вероятность ошибки будет просто огромна. И в итоге – как показывает опыт. Если в управлении находится джуниор то через како то время он начинает просто путаться в своем же Yii коде и поверьте – после этого в нем не разберется ни один программист. Проще будет переписать с 0. Статья не о том как сделать самое крутое современное приложение на метеоре. Статья о том какой подход для бизнеса будет лучшим по соотношению цена/качество. Подход на простом чистом PHP оправдан так как вы за пару часов найдете PHP программиста который допишет все необходимое за пару тысяч рублей. Если речь идет о приложении на метеоре или подобных разумного уровня разработчиков – я думаю сумма будет начинаться с 200. Если у вас стартап, нет денег а нужно срочно поменять концепцию это будет смерть для проекта. А мой подход даст даже новичку шанс все сделать более менее просто быстро и чисто. И в дальнейшем код будет как минимум логичен.
              +2
              Статья о том какой подход для бизнеса будет лучшим по соотношению цена/качество

              } else
                                              view::error("Incorrect data type for: " . implode(', ', $wrong_types), 204);
                                      } else
                                          view::error("Missing parameters: " . implode(', ', $missing_parameters), 204);
                                  } else
                                      view::error("Method in developing.", 503);
                              } else
                                  view::error("The method '" . $action . "' does not exist.", 204);
                          } else
                              view::error("No params received.", 204);
                      } else
                          view::error("Method was not received.", 204);
                  }
              

              Цена — пачка роллтона на семерых?
              Я уже писал — скупой платит дважды, пока джуниор не сможет хотя бы прочитать код библиотеки/фреймворка, то нечего его и подпускать сюда.
              Если в управлении находится джуниор то через како то время он начинает просто путаться в своем же Yii коде

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

              А так же свои правила, структура и документация. Подобрать норм фреймворк под задачу тоже важно.
              И если вы рассчитываете на то, что сторонний разработчик быстро въедет в проект — как раз популярные фреймворки очень полезны, т.к. многие и так знают их структуру или прочитают документацию и разберутся. Не могут разобраться — не стоит тогда и браться за такое
                +3
                все ваше высказывание от и до неверно. Понятно, что вероятность написания джуниором говнокода велика. А вот на чем он будет писать — на фреймворке, толкающему к светлому будущему, или на говнокоде, оканчивающем карьеру — разница есть.
              +2
              Скажите, сколько джуниоров способны моментально интерпретировать современные лучшие практики в рабочий не забагованный проект?

              Смотря кого мы называем джуниором. По хорошему это должен быть чувак, который уже знает основы CS и может спокойно без какой либо помощи на каком-либо фреймворке решать типовые задачи.
              Тут еще момент… большинство разработчиков-самоучек как-то не сильно пытаются понимать зачем придумали ту или иную штуку. Взять тот же ваш метод orm — вы ясно просто где-то слышали что ORM это для работы с базой данных но понятия не имеете что это такое. Другие же люди путают active record и orm, третьи считают что doctrine это data mapper и не замечают что там помимо этого еще куча всего (orm + unit-of-work + repository + entity manager).
              любому начинающему программисту, за минимальное время, развернуть свое API для мобильного или веб-приложения.

              Нечего "начинающим программистам" делать в коммерческой разработке. Пусть для начала поучатся.
              Притом, он будет наглядно понимать что и как в нем работает.

              Нет не будет. Он либо будет подражать фреймворкам, либо не будет замечать важных вещей потому что каждый день приходится возиться и допиливать свое детище.
              Да и скажите — сколько будут стоить услуги человека, который знаком с этими лучшими практиками не по наслышке.

              Если мы про рейт — то дорого конечно. А если мы про сроки разработки и риски — то дешевле чем джуниор. Намного практичнее взять одного-двух синьеров которые запилят MVP за пару месяцев и потом уже расширять команду джунами/мидлами, чем брать джуниоров, которые за те же пару месяцев напишут MVP, которое потом проще переписать чем расширять новым функционалом.
              В итоге — очень полезный для малого бизнеса инструмент позволяющий проверить гипотезу в течении суток.

              Silex, Lumen, Slim… Для этого всего есть микрофреймворки. Для чуть более зрелого прототипирования — Symfony 2.8 имеет возможность использовать фичу под названием micro-kernel, где отключено все лишнее. Да и инструменты типа Doctrine хоть и обладают неимоверно высоким уровнем сложности (я реально мало проектов знаю на PHP которые могут сравниться по сложности с доктриной) — но об этой сложности знать не нужно разработчику, он просто накидывает бизнес объекты, логику взаимодействия, а база данных появляется сама по себе. Самое то для MVP. А уже потом можно заниматься оптимизациями и т.д.
              Я сам не люблю компромиссы, однако, особенно в кризис, я думаю — это уместно.

              Вообще это целая проблема индустрии, хакеры-самоучки, которые считают что они умнее остальных. Если брать PHP то можно встретить синьера с опытом в 10 лет который ни разу не писал юнит тестов, не знает что такое TDD и т.д. а потом удивляется почему это его проекты фэйлятся, разрабатываются медленно, а количество багов привешает всякие разумные пределы.
                +1
                Однако, мой код позволяет любому начинающему программисту, за минимальное время, развернуть свое API для мобильного или веб-приложения.

                Еще по поводу этого пункта… Как думаете, почему "начинающих врачей" до интернатуры к пациентам не разрешают прикасаться? Или почему надо хотя бы ПТУ окончить для многих других специальностей?
                И почему-то именно в программировании, которое обычно связывают именно с интеллектом, люди почему-то думаю что можно прочитать статейку и методом тыка начать делать проекты за деньги. При этом… хоть мы жизнью и не рискуем (хотя отдельные люди, которые работают с микроконтроллерами и пишут софт для автомобилей например и жизнью других людей рискуют), мы обычно рискуем чужими деньгами.
                На моей памяти много прикольных стартапов умерло именно потому, что разработчики накосячили и в итоге товый функционал добавлялся ужасно долго. Типа писать чатик на PHP 2-3 месяца (вместо пары недель на ноде) и из-за факапа в архитектуре держать его на серваке который бернит $2К в месяц. А деньги то вложены и их уже не вернуть. Я знаю людей которые настолько верили в свои проекты, что закладывали дома (не в СНГ разумеется) что бы оплатить разработку и маркетинг своего проекта.
                А теперь задумайтесь. Вот пишите вы свои фреймворки и т.д. Вопервых ввести нового человека в команду будет теперь дорого. Во вторых уровень технического долга заоблачный. В третьих вместо того что бы фичи реализовывать вы пишите новый роутер, который завтра может уже не соответствовать требованиям бизнеса.
                  –4
                  У меня успешно запущено более 4 проектов по такой схеме и поверьте ни для одного еще не пришлось дописывать новые роутеры :) Нагрузки распределяются веб-сокеты подключаются. Напротив, у меня есть знакомый сделавший без какого либо опыта чат на ноде и теперь его «счастливые» обладатели отдают за хостинг от 50 тыс рублей в месяц вместо того что бы 1 раз заплатить за разумный код. Поверьте, то, что творится в проектах, бюджет которых не превышает миллиона рублей, очень и очень далеко от идеалов. Счастлив уже тот кто умудрился найти честного разработчика который не кинет и не провернет аферы с договором (и такое было.) А дальше – у всех свои монстры в голове. И поверьте, с моим фреймворков разбирается школьник за пару дней и умудряется до самого продакшена не косячить. А вот те школьники что пишут на популярных фреймворках – не понимают что они вообще делают. Думаете такие люди станут вчитываться в документацию? В лучшем случае возьмут основы а дальше пойдут своей мыслью. Попробуйте в россии найти добросовестного разработчика за 50-100 тыс рублей в месяц который будет владеть всем необходимым инструментарием. Адекватные ребята давно сидят на аутсорсе за 5000$ а вот в российских реалиях творится АД. :) Вы тут пафосно рассуждаете о идеальном коде, а сами наверное и не в курсе что творится на биржах разработчиков и что речи не идет о топах. Возможно, в статье стоило более точнее описать в каких именно случаях такой подход будет наиболее выгодным. Но извините, очень мало времени на бездумную полемику которая ни к чему не приведет. Удачи Вам, спасибо за внимание!
                    +1
                    Вы комерсант а не программист, да и Москва не вся Россия.
                    –4
                    Ах да! Спасибо Вам за Ваш безумно ценный вклад в сообщество!
                    Ваши переводы так безумно полезны и интересны! Что может быть лучше – беспроигрышный вариант перевести статью опытного разработчика и собрать плюсы не понимая даже основы концептов из статьи.
                    А разбор html шаблонов под ангуляр – Вы явно делитесь с миром чем-то гениальным. Обязательно подписался на Ваш блог, буду держать руку на пульсе. Жду новых статей! Еще раз спасибо, всего Вам доброго.
                      +1
                      какой красивый способ ведения дискуссии.
                        +2
                        Это называется: «когда кончаются аргументы, нужно использовать argumentum ad hominem». Если вы решили оценить человека, опираясь на текст, который он публиковал, пожалуйста, прибавьте к статьям на Хабре его аккаунт на Тостере. Он, в общем-то, довольно-таки солидный вклад сделал в сообщество. Вы зря его пытаетесь дискредитировать.
                  +13
                  познаем дзен в чистоте

                  Давненько я не видел настолько противоречащего содержимому статьи заголовка. Сразу пройдусь по основным моментам, что бы люди ни в коем случае не стали так делать а потом уже допишу полное ревью.
                  md5(crypt($this->args['password'],SALT)))) {

                  Ради бога, есть же Password Hashing API. Вопервых и соль для каждого пользователя должна быть своя, во вторых, если не знаете как ее нормально сгенерировать, доверьтесь реализации password_hash. То что у вас — это одна сплошная бесполезная хрень, с таким подходом можно просто plain password хранить.
                  @session_start();

                  здорово… что сказать...
                  elseif (($value === '0') or ($value === 0))
                                      $value = 0;

                  Что это еще за бред наркомана с приведением типов? Не говоря уже о том, что вы могли просто функции написать, а не загонять все в бесполезные классы.
                  public function orm($sql, $array, $type){

                  Доставило. Почитайте что такое ORM. У вас же просто обертка для работы с SQL. Причем вы даже до Table Data Gateway не доросли судя по всему.
                  Словом… грусть тоска. Если хотите — могу продолжить.
                    –4
                    Полностью согласен – содержание классов ни в коем случае не пример к использованию в своем проекте. Максимум из того, что может быть интересно: подход к организации рабочих файлов в приложении и распределение задач между ними.
                      +4
                      да половина кода http://php.net/manual/ru/function.spl-autoload-register.php велосипед автолодера. И у вас ошибка в приложение:
                          function __construct() {
                              try {
                                  include 'db.config.php';
                                  $this->_db->exec('SET NAMES utf8mb4');
                              } catch (PDOException $e) {
                      
                              }
                          }

                      вызов класса два раза
                      $db = new db();
                      // some code
                      $dbTest = new db();

                      Вызовит fatal error. И экскепшины плохо не обрабатывать. Ибо при возникновение ошибки вы даже не поймете где искать. Ну и @.
                        –2
                        Код вырван кусками и приведен до минимальной работоспособности опять же — статья не про чистый код и его оформление, а идею.
                    +4
                    Мне вырвало глаз эти строки,
                    $this->db = new db();$this->view = new view();$this->factory = new factory();

                    Статья потребовало времени, а результат срезан неаккуратным кодом, вам же обидно!


                    Уважайте разработчиков, пожалуйста, PSR появился затем, чтобы нам было комфортно друг с другом общаться,
                      0
                      Спасибо за комментарий – строчку подправил :)
                      +4
                      Нет ли тут PHP-inj?

                      include_once('api/methods/' . $action . '/parameters.inc.php');
                        –2
                        После проверки
                        if (file_exists('api/methods/'. $action. '/parameters.inc.php')) {
                        маловероятно наличие посторонних ссылок в переменной.
                          +5
                          Вы бы лучше не спорили, а дали нам ссылочку на сайт, где этот велосипед используется…

                          $action = "../../etc/passwd\0";
                            –1
                            $action = explode('/', $url['path']);

                            $action не может содержать слэши
                              +1
                              а кто мешает использовать обратные?)
                                –5
                                А зачем в URL обратные слэши?
                                  +5
                                  А зачем вообще программирование… ООП, фабрики всякие. Зачем динамические скрипты для общения с БД? Есть же статика)
                                  P.S. Ну, естественно, это ирония...
                                  +3
                                  Оказалось, что начиная с 5.3 нулевой символ зачем-то вырезается (на сервере стоит 5.6 — автор отправил мне ссылку). Обратные слэши действительно воспринимаются PHP как разделитель пути, но все портит parameters.inc.php в конце.

                                  В любом случае, через 8 сек. была найдена XSS. Поэтому я так «люблю» самописные движки.
                                    0
                                    Подклитесь информацией о уязвимости, пожалуйста.
                                      0
                                      Я же еще вчера написал вам в личку. Сейчас уточню еще раз.
                                    +1
                                    Предвосхищая минусы. Мой предыдущий коммент рассматривать дословно — как вопрос)
                                    0
                                    <?php die('konec') ?>

                                    такое же может содержать.
                                      0
                                      Любой текст может такое содержать :)
                                        0
                                        так вы инклуд делаете. А это значит что у вас интерпритатор выполнит '<?php die('123') ?>' т.к. вместо
                                        include('/controller/action/params') где action параметер будет что то вроде include('controller/<?php die('123') ?>/params') собственно die('123') должен выполниться.
                                          0
                                          Перед инклюдом проверка на существование дирректории с методом
                              +5
                              Основная проблема статьи в том, что она не о том, что написано в заголовке:
                              • почитайте разницу между REST и RPC и вы поймете, что у вас совсем не Restfull API
                              • скорее здесь, что-то противоположное чистоте, по крайне мере я такой код чистым и хорошо организованным назвать не могу, чего только стоит конструктор класса api
                              • код далёк от соответствия хорошим практикам разработки на php, думаю ещё во многом причина заключается в вашем стремлении игнорировать уже существующие наработки, которые развиваются большими сообществами, не стоит их бояться
                              • лично мне, ваш код оставляет ощущение, что это больше процедурный код, спрятанный под видом ООП, что может быть не лучшим примером для изучения новичкам

                              Мой совет — обратите внимание на советы в комментариях выше по улучшению вашего кода и не избегайте чужой код только потому что он чужой, это сильно поможет как вам в развитии так и вашим проектам.
                                0
                                Спасибо, буду знать!
                                +3
                                Кстати, никогда не понимал, зачем вот после return делают elseif / else. Это для надёжности? sarcasm
                                if(($var === 'true') || ($var === true))
                                  return true;
                                elseif(($var === 'false') || ($var === false))
                                  return true;
                                else
                                  return false;
                                  +6
                                  Меня в принципе этот кусочек кода смущает.
                                    0
                                    Ну про чудеса вроде… === 'false' Вы уже выше упомянули, поэтому я решил не дублировать)
                                    +2
                                    Аналогичная ситуация. Вдруг return не сработает?
                                    case 'smallint':
                                        return (is_numeric($var) && (strlen($var) == 1)) ? true : false;
                                        break;
                                      +5
                                      Настоящая жесть тут — это:
                                      return expression ? true : false;
                                        +2
                                        Да, это тоже можно отнести к загадкам мироздания...
                                          –2
                                          Очевидно, до упразднеия кода там что-то было.
                                            0
                                            Не уловил, в чем ошибка в данном куске.
                                              0
                                              Оператор && в любом случае даст булев результат, поэтому в тернарном операторе нет никакой необходимости.
                                              Ну со скобками, на мой взгляд, тоже небольшой перебор, но это, в общем-то, дело вкуса.
                                                +1
                                                В данном случае, получается что-то вроде:
                                                если блаблабла истина то вернуть истина
                                                иначе вернуть ложь

                                                Очевидно, что вместо:
                                                return condition ? true : false;

                                                Любой нормальный человек напишет:
                                                return condition;
                                                  0
                                                  Максимум return (bool)condition, если condition произвольного типа
                                                    0
                                                    В данном случае было достаточно:
                                                    return is_numeric($var) && strlen($var) == 1;
                                                    0
                                                    Я джуниором тоже так писал, пока мне не указали я и не заметил. Кстати, одного раза вполне достаточно, чтобы понять идею return
                                            0
                                            Ещё мне не очень понятно как планируется использовать вот эту штуку.
                                            print $query;
                                            die(json_encode(array("Error" => "Syntax error.")));
                                              –2
                                              Вывод SQL запроса с ошибкой. А далее: очередная защита от дурака. Что бы он не дописал дальше – это не выполнится. В продакшене, конечно, все голые принты должны быть убраны.
                                              +7
                                              >Ген. Директор Mettle Media

                                              Это правда?! С таким кодом?! В 2016-м?! о_О
                                                +3
                                                Вы сначала погуглите о компании =). Не стоит сразу хвататься за сердце.
                                                  +4
                                                  Если это вот эта "Студия". Мне страшно :( Правда у них в портфолио всего 4 проекта, но учитывая с кем они сотрудничают, мне страшно не за студию, а за эти компании. Надеюсь, что такое качество кода только у их директора и он не участвует в разработке. Пусть это будет так… Пожалуйста :)
                                                    +1
                                                    Кто то уже проверил на них XSS из статьи?
                                                      0
                                                      Вы правы, к стжалению для меня, мой подход уже устарел. Однако статья не столько про код, сколько про моделирование архитектуры, которая может быть описана любым современным подходлм на любом языке.
                                                        +2
                                                        Далее, для понимания всей картины, давайте окинем взглядом архитектуру будущего приложения:

                                                        и далее вы приводите картинку со структурой папок проекта.
                                                        структура директорий не равно архитектура приложения.
                                                          +2
                                                          Я бы сказал что этот подход устарел лет на ~10.
                                                            +3
                                                            лет на 30. MVC придумали 40 лет назад а тут даже разделения явного нет.
                                                          0
                                                          На сайте под большинством проектов явно обозначена моя роль в разработке :)
                                                        0
                                                        очевидно, что гендиректор когда-то был программистом. А программистом он был, судя по коду, около 10 лет назад.
                                                          –2
                                                          Вы не долеки от истины)
                                                            +1
                                                            хотя если вам 21, то мое предположение скорее неверно. Просто вы изначально неверно стартовали в профессии, а сейчас уже видимо не хотите переучиваться.
                                                              0
                                                              Не знаю о чем речь, но активно программированием я занимался как раз около 6 лет назад
                                                          +1
                                                          Ген. Директор в 22 года?
                                                            –2
                                                            В 21 :)
                                                              –1
                                                              Ну а что в этом такого? Я в 21 был директором, в 23 был свой бизнес. Возраст, в наше время, не влияет ни на что.
                                                              P.S.: вспомнил — у меня знакомый есть, который в 18 лет стал ген.директором. Правда, до этого он в организации работал больше 4х лет (не официально), да и организация была не большая (человек 12).
                                                            +6
                                                            Как теперь это развидеть? Простите.
                                                              0
                                                              Отвлекусь от статьи, выше все разложили. Другой вопрос: http://mettle.media/1 в портфолио… Таки правда?
                                                                0
                                                                Признаюсь, клиент был наш через третьих лиц, но. Таки да. Больше горжусь dhl и Axis bank.
                                                                  0
                                                                  Т.е. если что Avito не признает в вас своего подрядчика? Уверены, что в условиях сотрудничества нет пункта, согласно которому нельзя публично светить себя как субподрядчика? Ситуация вполне может быть серьезной. У коллег была ситуация, когда конечный заказчик (Москва) случайно узнал, что субподрядчик из региона. Несмотря на то, что проект был практически завершен и заказчика устраивал контракт был сразу разорван.
                                                                    0
                                                                    Контракт был маленький и давно закрыт. Руководство компании, а в частности технический отдел, в курсе и не были против.
                                                                +1
                                                                Куча вложенных условий плохая практика (низкая читабельность — больше вероятность ошибки). Оставляя за рамками вопрос адекватности кода привожу как можно такие конструкции писать "вертикально". Вот эта дикая N вложенность:
                                                                if (!empty($action)) {
                                                                
                                                                    if (!empty($_POST) || (substr($action, 0, 4) == 'get_')) {
                                                                
                                                                        ...
                                                                    } else
                                                                        view::error("No params received.", 204);
                                                                } else
                                                                    view::error("Method was not received.", 204);

                                                                легко сводиться к нулевой сложенности:
                                                                // [1- Проверка данных
                                                                if ( empty($action) ) {
                                                                    view::error('Method was not received.', 204);
                                                                
                                                                    return;
                                                                }
                                                                if (empty($_POST)
                                                                    || (substr($action, 0, 4) != 'get_')
                                                                ) {
                                                                    view::error('No params received.', 204);
                                                                
                                                                    return;
                                                                }
                                                                // ...и другие if-ы
                                                                // -1]
                                                                
                                                                // Отрабатываем логику
                                                                $parameters = array();
                                                                $missing_parameters = array();
                                                                $wrong_types = array();
                                                                // ...остальной код бизнес логики
                                                                  –1
                                                                  Полезный комментарий, спасибо! Постараюсь добавить в статью
                                                                  0
                                                                  Вы в курсе что делит и апдейт в sql возвращают довольно таки нужные значения? А вы их игнорируете :) Не надо так.
                                                                    0
                                                                    Конечно! Там именно для этого выделены кейсы – крайне рекомендую всем заинтересовавшимся дописать все необходимое для проекта.
                                                                    +4
                                                                    Вернули мне 2006
                                                                    • UFO just landed and posted this here
                                                                      +4
                                                                      Я посмотрел ваш код и у меня с рота сигарета выпала.
                                                                      Ваш код олицетворение шутки: "A piece of PHP code walks into a bar. The bartender informs it that they don't serve spaghetti there, so it turns around and leaves."
                                                                      Просто для развлечения рекомендую вам посчитать цикломатическую сложность методов вашего года.
                                                                      Ну и в целом это набор антипатерном и спагетти-кода, которое вы пафосно называете "идеальным решением". Мне кажется я вернулся в начало нулевых и посмотрел примеры кода на PHP 4.3
                                                                        +1
                                                                        Беги, дядь Мить...
                                                                          0
                                                                          Теперь понятно, почему в некоторых системах возникают проблемы: вроде той, что у Дженифер Нуль (Jennifer Null). Цитирую код из этой статьи:


                                                                                      case 'boolean':
                                                                                          if(($var === 'true') || ($var === true))
                                                                                              return true;
                                                                                          elseif(($var === 'false') || ($var === false))
                                                                                              return true;
                                                                                          else
                                                                                              return false;
                                                                                          break;

                                                                          Кто-нибудь наверное тоже там проверял: if ($var === Null || $var === 'Null').
                                                                            0
                                                                            Какая разница если они влзвращают в итоге только true. :)
                                                                              0
                                                                              А, ладно, я дополню код, чтобы было понятно, что я имел ввиду:
                                                                              if ($var === Null || $var === 'Null') {
                                                                                  return Null;
                                                                              }

                                                                              Хотя, я считаю, что и без этого понятно было, о чём речь.
                                                                                0
                                                                                Речь была про отношение к строкам, визуально похожим на данные другого типа, как к значениям этих типов данных. Строку «true» не нужно считать boolean-значением «true». Пользы особенно много такой подход не прибавляет, зато появляется точка непредсказуемости в коде.
                                                                              0
                                                                              Код, конечно, противно воняет, но не из-за этой проблемы, а из-за другой. Во-первых, бессмысленный if / elseif (если по обоим условиям возвращается один результат, зачем их было разделять?). Во-вторых, совершенно глупый и бессмысленный else return false (else не нужен). В-третьих, break, который никогда не будет выполнен. В-четвертых, автор реализовал функцию из стандартной библиотеки — is_bool.

                                                                              Andrey2008, посмотрите, сколько потенциальных клиентов у вас среди PHP-шников...
                                                                                0
                                                                                Я сразу после публикации этой статьи хотел своё мнение написать по коду. После того, как у меня комментарий по объёму добрался до объёма статьи, я его просто не стал публиковать. :)

                                                                                Я не говорил, что код воняет, и не говорил, что он воняет из-за этой проблемы. Я просто обратил внимание, что из-за данного конкретного подхода могут случаться нежданчики.

                                                                                А сам код из статьи в целом я не очень хочу обсуждать, потому что у меня есть более продуктивные занятия. С людьми, которых я могу контролировать, я работаю и объясняю, почему в коде лучше делать так или иначе. С писателями статей я не работаю и не критикую их код, потому что по большей части это будет как об стенку горох. В данном конкретном случае я вижу, что автор вообще не нацелен на то, чтобы из критики делать выводы. Так что, я считаю, что все, кто критикует код в данной статье, делают это лично для себя. :)

                                                                                Опять же, я не совсем понимаю, для чего вы мне объясняете, что «break» никогда не выполнится? Вы по какой-то причине думаете, что я этого не заметил самостоятельно, так? (Если чем-то эти вопросы обидно прозвучали, я искренне извиняюсь — у меня не было мыслей обидеть.)
                                                                                  0
                                                                                  Так что, я считаю, что все, кто критикует код в данной статье, делают это лично для себя.
                                                                                  Например, выше выяснилось, что не только автор этой статьи лишен способности видеть говно в коде. Кому-то пошло на пользу.

                                                                                  Опять же, я не совсем понимаю, для чего вы мне объясняете, что «break» никогда не выполнится?
                                                                                  Простите, что?! :-) Я пишу не вам в личку, а публичный комментарий, который логичнее всего смотрится именно в этом месте.
                                                                                    0
                                                                                    Я пишу не вам в личку, а публичный комментарий, который логичнее всего смотрится именно в этом месте.

                                                                                    Ну тогда относитесь к моим словам так же, как и к своим. Фраза «я не совсем понимаю, для чего вы мне объясняете, что «break» никогда не выполнится» — это просто публичный комментарий, который логично смотрится в том месте, где он был. Не знаю, с чего вы взяли, что он к вам был обращён. :)
                                                                                      0
                                                                                      Вообще, я буду иметь ввиду, спасибо. В следующий раз, если вы напишете ответ на мой комментарий, я обязательно переспрошу, мне вы это писали или всем.
                                                                                0
                                                                                del

                                                                                Only users with full accounts can post comments. Log in, please.