Статьи

APIfy Ваше старое приложение с Toro

Для Google Summer of Code 2014 я был выбран для проекта по созданию REST API для ATutor. ATutor имеет сотни тысяч строк кода, но написан на ядре PHP. Введение класса роутера PHP для API было необходимо, но нам нужно было что-то ненавязчивое. В этом посте мы обсудим основные части проекта. Для этого поста все примеры кода будут соответствовать моему форку репозитория ATutor (ссылки на файлы будут предоставляться при необходимости).

Примечание. Google Summer of Code — это программа, в которой студенты по всему миру могут участвовать в проектах менторских организаций с открытым исходным кодом. Google организует программу и выплачивает стипендии, но студенты нигде не работают в Google во время программы.

Веб-маршрутизация с Toro

Первым шагом в этом процессе было создание или запись класса PHP для выполнения маршрутизации. Рассмотрев несколько вариантов, мы решили пойти с Toro , облегченным маршрутизатором PHP. Он выполняет только маршрутизацию — ни больше, ни меньше. Синтаксис довольно интуитивно понятен, и вы начнете через несколько минут.

Toro — это RESTful — он поддерживает стандартные методы HTTP — GETPOSTPUTDELETE Также есть поддержка запросов на основе JSON. Все это упаковано в файл с нечетной строкой в ​​120 строк.

Прежде чем мы продолжим, еще одним шагом было настроить сервер для перенаправления всех запросов к маршрутизатору. Это можно сделать, добавив файл .htaccess в Apache или изменив файл конфигурации Nginx. Этот шаг настройки сервера описан в README репозитория Toro GitHub .

Программа Hello World

После того, как вы успешно настроили свой веб-сервер, вам просто нужно включить файл Toro.php Toro сопоставляет входящий шаблон URI и отправляет запрос обработчику для обработки. Давайте посмотрим на простейшую программу «Hello World» (с официального сайта Toro ).

 class MainHandler {
    function get() {
        echo "Hello, world";
    }
}
 
Toro::serve(array(
    "/" => "MainHandler",
));

Есть два блока кода, которые кажутся довольно интуитивными. Вы предоставляете Toro ассоциативный массив для обслуживания, ключи которого соответствуют шаблонам URL и значениям, содержащим классы обработчиков. Toro пытается сопоставить URI запроса с этим массивом и выполняет соответствующий обработчик, в зависимости от метода HTTP. В нашем примере вы получите желаемый результат «Hello, World», если отправите запрос GET в «/» (или в корневой каталог). Для запросов POSTPUTDELETEpost()put()delete()

Toro дает вам свободу кодировать, как вам нравится. Вы можете написать все обработчики в одном файле index.php Тем не менее, идеалом было разделить логику на полупроекты и иметь свои собственные каталоги, файлы для обработчиков и URL-адреса. В index.phparray_merge($urls1, $urls2, ...)

Отделение URL от остальной логики

Теоретически вы можете хранить весь свой код (обработчики, маршруты, вспомогательные классы и функции) на одной странице, но это может ухудшить читабельность. Для простоты крайне важно, чтобы мы разделили обработчики, маршруты и вспомогательные классы и функции. На самом деле, хорошей практикой является создание отдельных каталогов для разных приложений, которые вы собираетесь создать в проекте, каждое из которых имеет urls.phprouter_classes.php Дополнительные файлы, такие как tests.phphelper_functions.php

В файл index.phparray_merge Вот как это выглядит .

 Toro::serve(array_merge(
    $base_urls,
    $course_urls,
    $student_urls,
    $instructor_urls,

    // Include the url array of your app
    $boilerplate_urls
));

Необходимость префикса URL

Мы видели, что полезно создавать отдельные файлы urls.php

Вы можете заметить один маленький факт: URL-адреса каждого приложения начинаются с названия приложения. Поэтому префикс является общим для всех URL-адресов одного приложения. Вы должны определить префикс для приложения и добавить его в URL, чтобы вам не пришлось повторять его. Вот как выглядит типичный urls.php

 $instructor_url_prefix = "/instructors";

$instructor_base_urls = array(
    "/" => "Instructors",
    "/:number/" => "Instructors",
    "/:number/courses/" => "InstructorCourses",
    "/:number/courses/:number" => "InstructorCourses",
    "/:number/courses/:number/instructors" => "CourseInstructorList",
    "/:number/courses/:number/students" => "CourseEnrolledList"
);

$instructor_urls = generate_urls($instructor_base_urls, $instructor_url_prefix);

Вы должны заметить, что я иногда добавляю :number A :number:string:alpha Например, /instructors/5//instructors/5/courses/6/

С целью добавления префикса ко всем URL-адресам я создал пользовательскую функцию generate_urls Вы можете взглянуть на это под моими основными вспомогательными функциями .

Аутентификация пользователя

Любой API, который вы разрабатываете, должен иметь возможность аутентифицировать пользователей. Один из способов сделать это — использовать токены. Вот несколько замечаний о токенах для аутентификации пользователей.

  • Генерировать токен при успешном входе в систему. В этом случае я генерирую токен, беря хэш комбинации имени пользователя, метки времени и случайного числа.
  • Задайте срок действия токена, указав отметку времени истечения, после которой токен будет недоступен.
  • Маркер присутствует в заголовке каждого запроса.
  • Измените срок действия токена при каждом успешном запросе и обновите отметку времени истечения.
  • Если срок действия токена истек, удалите запись или выполните эквивалентную задачу (например, установите параметр в базе данных, в котором токен будет указан как просроченный).

Разработка функции позвоночника

При разработке API вы заметите после реализации первых нескольких функций, что процесс для каждого вызова API очень похож. Проще говоря, каждый вызов API проверяет, является ли токен действительным, проверяет, имеет ли пользователь доступ к этому ресурсу, выполняет SQL-запрос (или более) и печатает результат. Поэтому можно написать магистральную функцию для выполнения всех таких запросов. Вот особенности такой функции.

  • Проверьте действительность токена и определите, какой пользователь соответствует этому токену.
  • Проверьте, доступен ли доступ к ресурсу для пользователя; поднять ошибку во всех остальных случаях.
  • В случае создания объектов вернуть идентификатор вновь созданного объекта.
  • В случае запросов на изменение или удаление объектов, проверьте, существует ли объект, прежде чем удалять или изменять его.
  • Распечатайте ответ и зарегистрируйте запрос.

Функцию, которую я разработал для своего проекта, можно найти на GitHub .

Повторное использование классов для извлечения похожих объектов

Представьте, что вы создаете API, в котором вам нужно получить пользователей в вашей системе. Посмотрите на следующие типы URL.

 "/members/" => "Class1",
    "/members/:number/" => "Class2",

Первый URL-адрес дает вам список участников, а второй дает вам информацию о конкретном члене. Запросы SQL, которые будут выполняться в этих двух случаях, будут очень похожи — с простым WHERE Мы можем повторно использовать один и тот же класс MySameClass

 class MyClass {
    function get($member_id) {
        
        ...

        if ($member_id) {
            $query = $connection->prepare("SELECT col1, col2, col3 FROM table_name WHERE id = :member_id");
            $query->bindParam(':member_id', $member_id, PDO::PARAM_INT);
        }
 
        // Execute the query or do something else
        $query->execute();
        $result = $query->fetchAll();

        ...
    }
}

Простая логика заключается в том, что в случае первого URL-адреса $member_idWHERE

Последние мысли

В этой статье я рассмотрел основную идею создания API в PHP с использованием Toro в качестве маршрутизатора. Я сосредоточился на нескольких областях, которые могут повлиять на эффективность и удобочитаемость кода, который вы пишете. То, о чем я говорил, может быть не единственным способом выполнения определенных действий, и у вас могут быть даже лучшие идеи выполнения того же действия.

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