LINUX.ORG.RU

Оцените PHP-класс для роутинга

 


0

1

привет, друзья. пишу CMS с нуля, назвал её hacked)))

делаю роутинг. оцените пожалуйста.

API для работы выглядит следующим образом:

Route::register(
	[
		'/' => 'Home',
		'/user/$id/profile' => function ($id) {echo "Вы указали " . (int) $id . " в адресной строке";},
		'/blog/' => 'Blog::Index',
		'catch_all' => function () {die('404 - Not found.');}
	]
);

Route::run('/');

Метод Route::register принимает массив с данными в формате «путь» => «класс», «метод», «функция» или «омнонимная функция».

Если указать просто класс, то будет вызван метод $_SERVER['REQUEST_METHOD']. Например, если мы выполняем GET-запрос странички /home/, которая ссылается на класс Home, то будет выполнен new Home()->get(); соответственно для post будет вызван метод new Home()->post(); и так далее. Поэтому самому указывать GET, POST и т.д. нигде не нужно.

Так же, в URL можно передавать параметры функциям, например URL вида «/user/$id/profile» запишет в переменную $id реальное значение параметра из URL

В случае, если запрошенный адрес не соответствует ни одному из указанных шаблонов в массиве Route::register([]);, то будет предложено вызвать содержимое 'catch_all' из массива. Например, туда можно записать 404 ошибку.

Ну и в довершении всего, метод Route::run(); запускает на выполнение заданный REQUEST_URI.

Route::run('/');

Route::run('/blog/something-else-string/');

Route::run(strtok($_SERVER['REQUEST_URI'], '?'));

Вы можете сами указать там любой URI, таким образом из любого места в любом скрипте вызвать любую страничку. А можете оставить стандартный $_SERVER['REQUEST_URI'].

<?php

class Route {
	static protected $instance = null;

	static private $routes = [];

	public function __construct() {}
	public function __destruct() {}
	public function __clone() {}

	static public function register($routes) {
		if (self::$instance == null) {
			self::$instance = new self;
		}

		if (is_array($routes)) {
			self::$routes = $routes;
		}
	}

	static public function run($route) {
		if (self::$instance == null) {
			self::$instance = new self;
		}

		$request_method = strtolower($_SERVER['REQUEST_METHOD']);
		$discovered_handler = null;
		$handler_instance = null;
		$catch_all = true;
		$path_args = [];
		$path_parts = explode('/', $route);

		foreach (self::$routes as $pattern => $handler_name) {
			$uri_parts = explode('/', $pattern);
			$uri_args = [];
			$uri_rebuild = [];

			for ($i = 0; $i < sizeof($uri_parts); $i++) {
				if (isset($path_parts[$i])) {
					if (substr($uri_parts[$i], 0, 1) == '$') {
						$name = substr($uri_parts[$i], 1);
						$uri_args[$name] = $path_parts[$i];
						$uri_rebuild[] = $uri_parts[$i];
					}
					elseif ($path_parts[$i] == $uri_parts[$i]) {
						$uri_rebuild[] = $uri_parts[$i];
					}
				}
			}

			$uri_rebuild = implode('/', $uri_rebuild);

			if (isset(self::$routes[$uri_rebuild])) {
				$discovered_handler = $handler_name;
				$path_args = $uri_args;
			}
		}

		if ($discovered_handler) {
			if (is_string($discovered_handler)) {
				if (class_exists($discovered_handler)) {
					$handler_instance = new $discovered_handler();
				}
			}
			elseif (is_callable($discovered_handler)) {
				$handler_instance = $discovered_handler();
			}
		}

		if ($handler_instance) {
			if (method_exists($handler_instance, $request_method)) {
				$catch_all = false;
				call_user_func_array([$handler_instance, $request_method], $path_args);
			}
		}

		if ($catch_all) {
			if (isset(self::$routes['catch_all'])) {
				self::run('catch_all');
			}
		}

	}
}

★★★★★

Последнее исправление: Spoofing (всего исправлений: 4)

Ответ на: комментарий от izzholtik
public function __construct() {}
	public function __destruct() {}
	public function __clone() {}

Как-бы намекает.

А конструктор не закрыт.

Доберусь до компьютера, послтрю нормально.

fernandos ★★★
()

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

Роутинг вообще не нужен, если тебе нужен /takoy-razdel/ то создай /var/www/takoy-razdel/index.php.

Но тебе нужен будет API для фронта, вот тут я предлагаю тебе революционную схему.

user.php

<? require_once $_SERVER['DOCUMENT_ROOT'] . '/bestcms/startpage.php'; ?>

<? CApiManager::Handle([
  // @ - проверка на авторизацию отключается
  // sLogin - проверка типа по венгерской нотации
  // $sLogin - автоматическое прокидывание аргументов
  "@register|sLogin,sPassword" => function($sLogin, $sPassword) {
    ...
   return $result; // преобразуется в json
  },
  // проверка на авторизацию автоматическая
  // еще бы не забыть про csrf
  "user_info" => function() {
    ...
  },
  "change_password|sOldPassword,sNewPassword" => function($sOldPassword, $sNewPassword) {
    ...
  }
]); ?>

<? if(CUser::IsAuthorized()): ?>
  <!-- Профиль -->
<? else: ?>
  <!-- Регистрация -->
<? endif; ?>

<? require_once $_SERVER['DOCUMENT_ROOT'] . '/bestcms/endpage.php'; ?>

На одной странице и API, и отображение. Тяжелая логика выносится естественно в отдельные файлы.

CApiManager::Handle смотрит $_REQUEST, и если там есть BESTCMS_API=Y, то обрабатвыает запрос сам, не находит функцию - выбрасывает ошибку.

MOPKOBKA ★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 2)
Ответ на: комментарий от MOPKOBKA

человек в стильном дорогом костюме фигни не посоветует.

Spoofing ★★★★★
() автор топика
Ответ на: комментарий от fernandos

с компьютера оно ещё страшнее - видно, что синглтон инициализируется вручную при вызове всех публичных методов, что все члены класса статические, и потому конструирование нового объекта не имеет смысла, и что instance вообще нигде не используется..)
Я ни разу не PHPшник, но сабж выглядит потешно.

izzholtik ★★★
()

Так не люблю цикл в цикле, хотя иногда нет выбора. Тут точно нет выбора?

foreach (self::$routes as $pattern => $handler_name) {
  for ($i = 0; $i < sizeof($uri_parts); $i++) {
    if (isset($path_parts[$i])) {
     if (substr($uri_parts[$i], 0, 1) == '$') {...}
    }
  }
}

Ифак в ифаке и ифаком погоняет:

if ($discovered_handler) {
  if (is_string($discovered_handler)) {
    if (class_exists($discovered_handler)) {...}
  }
  elseif (is_callable($discovered_handler)) {...}
}
CryNet ★★★★★
()

Так-то жесть конечно, но лишь бы тебе нравилось. Иначе непонятно, зачем нужно писать то, что уже 100500 раз написано.

no-such-file ★★★★★
()

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

anonymous
()
Ответ на: комментарий от izzholtik

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

fernandos ★★★
()
Ответ на: комментарий от MOPKOBKA

За .vue тоже банишь?

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

anonymous
()
Ответ на: комментарий от no-such-file

зачем нужно писать то, что уже 100500 раз написано

Так неправильно же написано

Psilocybe ★★★★
()

делаю роутинг. оцените пожалуйста.

Это ужасно

Psilocybe ★★★★
()
Ответ на: комментарий от MOPKOBKA

Ненужно

Ненужно писать в авторском стиле.

Может еще и от венгерской нотации отказаться?

Заменив её нормальным описанием переменной. Добро пожаловать в 21-й век.

fernandos ★★★
()
Ответ на: комментарий от fernandos

Ненужно писать в авторском стиле.

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

Заменив её нормальным описанием переменной. Добро пожаловать в 21-й век.

/** 
  * @var string $variable
  */
$variable = "Hello World";

vs

$sVariable = "Hello World";
Прогресс как всегда.

MOPKOBKA ★★★
()
Ответ на: комментарий от MOPKOBKA

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

Чтобы никто кроме вас это не мог читать, ага.

@var string $variable

Нет.

/** 
 * @var string описание переменной
 */
$variable = "Hello World";

А как вы декларируете типы в массиве? И, кслову, как вы псалму или пхпстану объяснили, что переменная, начинающаяся с s — строка?

fernandos ★★★
()
Ответ на: комментарий от fernandos

Чтобы никто кроме вас это не мог читать, ага.

Ты правда не можешь прочесть код выше?

Нет.

Какая разница, твой метод в 10 раз длиннее.

А как вы декларируете типы в массиве?

Обычно никак.

И, кслову, как вы псалму или пхпстану объяснили, что переменная, начинающаяся с s — строка?

Не пользуюсь.

MOPKOBKA ★★★
()
Ответ на: комментарий от MOPKOBKA

Ты правда не можешь прочесть код выше?

После 6-10 часов просмотра хорошодокументированного кода, ваш прочитать будет сложно.

Какая разница, твой метод в 10 раз длиннее.

Его большая часть дополняется редактором кода. Зато я получаю универсальный метод документирования переменной. А вы просто запутаетесь в префиксах, когда дело дойдёт до классов.

fernandos ★★★
()
Ответ на: комментарий от izzholtik

Почему? Или у вас тоже уникальное авторское видение правильности?

Ну и вы понимаете, что это неверный докблок?

fernandos ★★★
()
Последнее исправление: fernandos (всего исправлений: 1)

привет, друзья.

Спуфи, раз ты так начал, то даю совет, но только между нами: следует тратить час в день на образование. Свежие/надёжные практики от коллег тебе сильно помогут.

anonymous
()

Как эксперимент, нормально, но так есть куда расти. Статика - зло и замедленная мина, советую использовать подсказки типов и strict types, магические константы (catch_all) превратить в настоящие константы, непостоянную $_SERVER засунуть в инициализацию Request объекта и в классе использовать уже него, лесенка IFов и FORов больше двух уровней - слишком сложно, стоит упростить, наверно разбив один класс на несколько и используя ранний возврат.

anonymous
()

Надо сделать так

Route::register(
	[
		'$id' => {die('Hello Spoofy!!!');},
		'/' => 'Home',
		'/user/$id/profile' => function ($id) {echo "Вы указали " . (int) $id . " в адресной строке";},
		'/blog/' => 'Blog::Index',
		'catch_all' => function () {die('404 - Not found.');}
	]
);

Psilocybe ★★★★
()

Чего люди только не делают, лишь бы не писать на Go. С пхп бежит уже большинство компаний на Go, Node.JS

menangen ★★★★★
()
Ответ на: комментарий от menangen

Бежать на node.js это поменять шило на мыло

cobold ★★★★★
()
Ответ на: комментарий от menangen

Та PHP не умрёт никогда и всегда будет восстребован, как и JS, собственно.

А под Go уже есть фреймворки типа Leraver и т.д? Или они не нужны под Go?

CryNet ★★★★★
()
Ответ на: комментарий от fernandos

ПСР нарушен

тссс, вспугнеш
я только попкорн притащил

kiotoze ★★★★
()
Ответ на: комментарий от izzholtik

Но ведь больше одного экземпляра роутера никогда не может понадобиться. </sarcasm>

theNamelessOne ★★★★★
()
Последнее исправление: theNamelessOne (всего исправлений: 1)

А что, твой роутер разве не умеет роутить по HTTP-методу?

theNamelessOne ★★★★★
()

Хотел пошутить про год и стек, да и саму задачу про дохлый труп такого класса ПО как CMS.

Но автор Спуф. А он Лору как дитятко. Чем бы не баловалось.

system-root ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.