Verification: a143cc29221c9be0

Php clone object to this

Introduction

PHP is currently having problems with RNG reproducibility.

PHP's RNG has been unified into an implementation using the Mersenne twister, with the rand() and srand() functions becoming aliases for mt_rand() and mt_srand() respectively in PHP 7.1.

But, these functions still store the state in the global state of PHP and are not easily reproducible. Look at the following example.

echo foo(1234, function (): void {}) . PHP_EOL; // Result: 1480009472
echo foo(1234, function (): void { mt_rand(); }) . PHP_EOL; // Result: 1747253290
 
function foo(int $seed, callable $bar): int {
    mt_srand($seed);
    $result = mt_rand();
    $bar();
    $result += mt_rand();
    return $result;
}

As mentioned above, the reproducibility of random numbers can easily be lost if additional processing is added later.

In addition, the fiber extension was introduced in PHP 8.1. This makes it more difficult to keep track of the execution order. However, this problem has existed since the introduced of Generator.

There is also the problem of functions that implicitly use the state stored in PHP's global state. shuffle(), str_shuffle(), and array_rand() functions implicitly advance the state of a random number. This means that the following code is not reproducible, but it is difficult for the user to notice this.

mt_srand(1234);
echo mt_rand() . PHP_EOL; // Result: 411284887
 
mt_srand(1234);
str_shuffle('foobar');
echo mt_rand() . PHP_EOL; // Result: 1314500282

Proposal

Implement and bundled Random extension into PHP.

The pseudo-implementation for the whole extension is as follows:


 
namespace Random
{
    interface NumberGenerator
    {
        public function generate(): int;
    }
}
 
namespace Random\NumberGenerator
{
    class XorShift128Plus implements Random\NumberGenerator
    {
        public function __construct(?int $seed = null) {}
        public function generate(): int {}
        public function __serialize(): array {}
        public function __unserialize(array $data): void {}
    }
 
    class MT19937 implements Random\NumberGenerator
    {
        public function __construct(?int $seed = null) {}
        public function generate(): int {}
        public function __serialize(): array {}
        public function __unserialize(array $data): void {}
    }
 
    class Secure implements Random\NumberGenerator
    {
        public function __construct() {}
        public function generate(): int {}
    }
}
 
namespace
{
    final class Random
    {
        private Random\NumberGenerator $rng;
 
        public function __construct(?Random\NumberGenerator $rng = null) {}
        public function getNumberGenerator(): Random\NumberGenerator {}
        public function getInt(int $min, int $max): int {}
        public function getBytes(int $length): string {}
        public function shuffleArray(array $array): array {}
        public function shuffleString(string $string): string {}
        public function __serialize(): array {}
        public function __unserialize(array $data): void {}
    }
}

Each RNG is implemented as a class in the Random\NumberGenerator namespace. They all implement the Random\NumberGenerator interface.

The bundled RNGs are as follows:

  • Random\NumberGenerator\XorShift128Plus: 64-bit, reproducible, PRNG.

  • Random\NumberGenerator\MT19937: 32-bit, reproducible, PRNG, compatible mt_srand() / mt_rand().

  • Random\NumberGenerator\Secure: 64-bit, non-reproducible, CSPRNG, uses php_random_bytes() internally.

Random class use a XorShift128+ by default. It can generate 64-bit values, is used by major browsers, and is fast and reliable. However, when used XorShift128+ in a 32-bit environment, the upper 32 bits are always truncated. This means that compatibility cannot be maintained between platforms, but this is not a problem since most platforms running PHP today are 64-bit and MT19937 can be used explicitly if compatibility is required.

Note that (new Random\NumberGenerator\MT19937($seed))->generate() requires an additional bit shift to get a result equivalent to mt_rand(). mt_rand() implicitly did the bit-shifting internally, but there was no obvious reason for this.

Secure is practically equivalent to random_int() and random_bytes(), This is useful when secure array or string shuffling is required.

This class also supports RNGs defined in userland. It can be used by passing an instance of a class that implements the Random\NumberGenerator interface provided at the same time as the first argument.This is useful for unit testing or when you want to use a fixed number.

class UserDefinedRNG implements Random\NumberGenerator
{
    protected int $current = 0;
 
    public function generate(): int
    {
        return ++$this->current;
    }
}
 
function foobar(Random\NumberGenerator $numberGenerator): void {
    for ($i = 0; $i  9; $i++) {
        echo $numberGenerator->generate();
    }
}
 
foobar(new UserDefinedRNG()); // Results: 123456789

Also, as with MT, various alternative APIs using Random class will be provided.

The Random class can be serialized using the standard PHP serialization mechanism. But, if the $rng member is not serializable, it will throws Exception.

// serialize
$foo = new Random(new Random\Numbergenerator\XorShift128Plus());
for ($i = 0; $i  10; $i++) { $foo->getInt(PHP_INT_MIN, PHP_INT_MAX); }
var_dump(unserialize(serialize($foo))->getInt(PHP_INT_MIN, PHP_INT_MAX) === $foo->getInt(PHP_INT_MIN, PHP_INT_MAX)); // true
 
// can't serialize
$foo = new Random(new Random\Numbergenerator\Secure());
for ($i = 0; $i  10; $i++) { $foo->getInt(PHP_INT_MIN, PHP_INT_MAX); }
var_dump(unserialize(serialize($foo))->getInt(PHP_INT_MIN, PHP_INT_MAX) === $foo->getInt(PHP_INT_MIN, PHP_INT_MAX)); // throws Exception:  Serialization of CLASS is not allowed.

It is not possible to clone the Random class. it always throws Error (Error: Trying to clone an uncloneable object of class Random). This is because the standard PHP clone method copies the members by reference when cloning. This will be an unintended behavior for most users. Instead, you can use the getNumberGenerator() method to retrieve the internal RNG instance. The RNG instance can be cloned.

$foo = new Random();
 
// can't direct clone
// $bar = clone $foo;
 
// safe
$bar = new Random(clone $foo->getNumberGenerator());

Using this feature, the first example can be rewritten as follows:

echo foo(1234, function (): void {}) . PHP_EOL; // Result: 1480009472
echo foo(1234, function (): void { mt_rand(); }) . PHP_EOL; // Result: 1480009472
 
function foo(int $seed, callable $bar): int {
    $numberGenerator = new Random\NumberGenerator\MT19937($seed);
    $result = ($numberGenerator->generate() >> 1); // requires bit-shift for compatibility.
    $bar();
    $result += ($numberGenerator->generate() >> 1); // requires bit-shift for compatibility.
    return $result;
}

Future Scope

This RFC will be the basis for making PHP RNGs safe in the future.

By first accepted this RFC, PHP gets a random number in the local scope.

The Random class can also be used when new features are implemented that use random numbers. This has the effect of discouraging more implementations from using random numbers that depend on the global scope.

More in the future, we can consider doing away with functions such as mt_srand(). These functions are simple and convenient, but they may unintentionally create implementations that depend on global scope.

Backward Incompatible Changes

The following class name will no longer be available:

  • “Random”

  • “Random\NumberGenerator”

  • “Random\NumberGenerator\XorShift128Plus”

  • “Random\NumberGenerator\MT19937”

  • “Random\NumberGenerator\Secure”

Proposed PHP Version(s)

8.1

RFC Impact

To SAPIs

none

To Existing Extensions

none

To Opcache

none

New Constants

none

php.ini Defaults

none

Open Issues

none

Vote

Voting opens 2021-MM-DD and 2021-MM-DD at 00:00:00 EDT. 2/3 required to accept.

потребность:

Студенты-бэкенды, которые писали интерфейсные документы для коллег-клиентов, вспомнили кровь и слезы рукописных документов, прежде чем использовать автоматизированные инструменты документирования в различных случаях.
Моя история отличается, потому что, во-первых, я возглавляю команду Android в компании и принадлежу к лагерю клиентов в упомянутой выше истории крови и слез.
Но история крови и слез взаимосвязана. В дни без автоматизированного документирования интерфейс - наименее эффективное звено в процессе разработки.
Поэтому я решил использовать Swagger для построения процесса генерации документов из комментариев PHP.

задний план:
Наш проект restful api использует фреймворк phalcon, общая структура очень проста, нам нужно только сканировать каталог контроллера с помощью swagger.
, в дальнейшем именуемый нашим проектом php api, называется php_api_project.
Сервер использует nginx.

Сборка:

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

  1. Напишите формат swagger / ** комментарий * / в php файл
  2. Используйте команду bin / swagger.phar в swagger-php, чтобы просканировать каталог, в котором находится контроллер php, и сгенерировать файл swagger.json.
  3. Скопируйте файл swagger.json в каталог, указанный index.html в swagger-ui.
  4. Откройте URL-адрес, по которому находится swagger-ui, и вы увидите документ. К каждому API в документе можно получить прямой доступ по URL-адресу для получения данных.

Для выполнения этого требования необходимы только два следующих элемента чванства:
swagger-php: Инструмент для сканирования комментариев php, хороший пример прилагается.
swagger-ui: Он используется для отображения содержимого файла swagger.json, созданного инструментом сканирования, на веб-странице.

Сначала загрузите эти два проекта на локальный компьютер:

$ git clone https://github.com/swagger-api/swagger-ui.git
$ git clone https://github.com/zircote/swagger-php.git

Развертывание инструмента генерации документов:

Говоря о развертывании, в основном это создание bin / swagger, команды, используемой для создания файла swagger.json.
Основная задача - использовать композитор для решения зависимостей.
Поскольку использовать композитор напрямую в Китае больно, лучше всего установить источник композитора в Китае.
В этом случае развертывание всего инструмента создания документов представляет собой следующие три строки команд:

$ cd swagger-php
$ composer config repo.packagist composer https://packagist.phpcomposer.com
$ composer update

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

$ cd swagger-php
$ mkdir json_docs
$ php ./bin/swagger ./Examples -o json_docs/

Приведенная выше команда просканирует комментарии файла php в каталоге примеров, а затем сгенерирует файл swagger.json в каталоге json_docs.
Этот файл swagger.json - это наш файл документа api, используемый интерфейсным интерфейсом swagger-ui для отображения.
NOTE: swagger-php - это просто инструмент, вы можете положить его куда угодно.

Развертывание внешнего интерфейса swagger-ui:

Метод развертывания очень прост, всего три шага:

1. Скопируйте папку dist в проекте swagger-ui в корневой каталог php_rest_api.

NOTE1: Вам нужно только скопировать папку dist. Лучше ее переименовать. Для простоты я не буду ее здесь переименовывать.
NOTE2: Корневой каталог нашего проекта и корень конфигурации nginx - это один и тот же каталог. Фактически, нет необходимости помещать каталог, просто поместите его в каталог, который не требует междоменного доступа. Почему существует перекрестный проблема домена? Я расскажу об этом позже.

2. Измените файл index.html в папке dist и укажите каталог, в котором находится swagger.json.

Просто измените одну строку.
Для простоты здесь вы можете напрямую указать каталог swagger.json в каталоге dist. Повторим здесь условия по умолчанию:
Предполагается, что хост проекта php_api_project - api.my_project.com;
Предполагается, что корень, указанный в nginx для проекта php_api_project, является его корневым каталогом;
Предполагается, что папка dist в swagger-ui размещена в указанном выше корневом каталоге;
Предполагая, что файл swagger.json будет размещен в указанном выше каталоге dist (php_api_project / dist / swagger.json);
Затем измените следующий фрагмент в index.html на этот:

      var url = window.location.search.match(/url=([^&]+)/);
      if (url && url.length > 1) {
        url = decodeURIComponent(url[1]);
      } else {
                 
        url = "http://api.my_project.com/dist/swagger.json";
      }

3. Скопируйте swagger.json в указанный выше каталог.

# Замените swagger-php_dir своей записью swagger-php
cp swagger-php_dir/json_docs/swagger.json php_api_project/dist/

После выполнения вышеуказанных шагов посетитеhttp://api.my_project.com/dist/index.html Вы можете увидеть api-документацию небольшого проекта Примеры.

Напишите комментарии PHP:

В примере проекта swagger-php уже есть много связанных примеров, просто следуйте инструкциям по копированию и вставке.
Для получения более подробной документации, связанной с правилами аннотации, см. здесь:
http://bfanger.nl/swagger-explained/#/swaggerObject

Предполагая, что каталог моего контроллера проекта - это php_api_project / controller /, тогда мне нужно только сканировать этот каталог, а не сканировать весь проект php.
Чтобы создать некоторую унифицированную конфигурацию в swagger.json, создайте каталог php_api_project / controller / swagger. В этом каталоге хранится файл php без кода, и в нем записываются только комментарии.
Я назвал этот файл Swagger.php, и его общее содержание выглядит следующим образом:


 В будущем каждый сможет с радостью подобрать интерфейс! 
В будущем каждый сможет с радостью подобрать интерфейс!
В будущем каждый сможет с радостью подобрать интерфейс!
" * ), * * @SWG\Tag( * name="User", * description = "Операция пользователя", * ), * * @SWG\Tag( * name="MainPage", * description = "Модуль домашней страницы", * ), * * @SWG\Tag( * name="News", * description = "новостная информация", * ), * * @SWG\Tag( * name="Misc", * description = "Другие интерфейсы", * ), * ) */

Как показано выше, в моем файле php нет строки кода php, только комментарии, чтобы определить некоторые глобальные настройки swagger:
schemes: Используйте соглашение (вы можете заполнить несколько соглашений)
host: Адрес проекта, этот адрес будет использоваться в качестве базы URL-адресов каждого интерфейса, объединенного на один период в качестве адреса доступа.
consumes: Тип MIME, полученный интерфейсом по умолчанию. FormData в моем примере соответствует типу формы сообщения. Обратите внимание, что это значение проекта по умолчанию. Это значение можно перезаписать в одном комментарии интерфейса.
produces: MIME-тип ответа по умолчанию для интерфейса. Наиболее часто используемый интерфейс api -application/json с участием application/xml.
@SWG\Info: Заполненный материал будет помещен в начало документа и использован как описание документа.
@SWG\Tag: Тег используется для классификации документов, и поле имени должно быть уникальным. Интерфейс может указывать несколько тегов, тогда он будет отображаться в нескольких группах категорий. Тег также можно использовать без предварительного определения здесь, но это все. Описание: Больше говорить бесполезно, но все поймешь с небольшим толком.

Затем писать комментарии в формате чванства для каждого интерфейса. Давайте каштан:

    /**
     * @SWG\Post(path="/user/login", tags={"User"},
     * summary = "Интерфейс входа (имя пользователя + пароль)",
           * description = "Интерфейс входа в систему, номер учетной записи может быть именем пользователя или номером мобильного телефона. Ссылка (на странице будет создана ссылка для перехода: [Примечания для входа в систему] (http://blog.csdn.net/liuxu0703 /) ",
     *   @SWG\Parameter(name="userName", type="string", required=true, in="formData",
           * description = "Имя пользователя для входа / номер мобильного телефона"
     *   ),
     *   @SWG\Parameter(name="password", type="string", required=true, in="formData",
           * description = "Пароль для входа"
     *   ),
     *   @SWG\Parameter(name="image_list", type="string", required=true, in="formData",
     *     @SWG\Schema(type="array", @SWG\Items(ref="#/definitions/Image")),
           * description = "Фотоальбом пользователя. Ну, никто не будет просить при входе в систему заполнять кучу информации об изображении. Это, например, данные со структурой @SWG \ Schema, эту структуру нужно определять отдельно, я расскажу об этом ниже ".
     *   ),
     *   @SWG\Parameter(name="video", type="string", required=true, in="formData",
     *     @SWG\Schema(ref="#/definitions/Video"),
           * description = "Пользователь э ... видео? То же, что и выше, например @SWG \ Schema."
     *   ),
     *   @SWG\Parameter(name="client_type", type="integer", required=false, in="formData",
           * description = "Тип клиента, вызывающего этот интерфейс: 1-Android, 2-IOS. Не требуется, поэтому требуется - false"
     *   ),
     *   @SWG\Parameter(name="gender", type="integer", required=false, in="formData",
     *     default="1",
           * description = "Пол: 1-мужской; 2-женский. Обратите внимание, что значение по умолчанию этого параметра не является значением параметра по умолчанию, а значением, которое будет по умолчанию заполнено на странице swagger, чтобы облегчить использование чванства для доступа к интерфейсу ".
     *   ),
     * )
     */
    public function loginAction() {
        // php code
    } 

    /**
     * @SWG\Get(path="/User/myWebPage", tags={"User"},
     *   produces={"text/html"},
           * summary = "Личная страница пользователя",
           * description = "Это не интерфейс api, он возвращает страницу, поэтому производит запись text / html",
     *   @SWG\Parameter(name="userId", type="integer", required=true, in="query"),
     *   @SWG\Parameter(name="userToken", type="string", required=true, in="query",
           * description = "Токен пользователя",
     *   ),
     * )
     */
    public function myWebPageAction(){
        // php code
    }

Правила просты и понятны, каждый понимает код, глядя на код. Если не понимаете, переходите к документации ...
В приведенном выше интерфейсе входа в систему используются два структурированных данных: один представляет собой массив типа изображения, а другой - структуру типа видео.
(Фактически, структурированные параметры могут быть толькоin="body" Его можно использовать только тогда, когда, но это не мешает нам форматировать структурированные данные как json и передавать их в виде строки, чтобы упростить проблему. Нам нужно только показать эту структуру в документе.)
Этот тип структурированного чванства также можно определить с помощью комментариев php:

Таким образом, когда эти два класса также сканируются swagger-php / bin / swagger, на две структуры с именами Image и Video можно правильно ссылаться в другом месте.
Преимущество этого заключается в том, что эта структура будет отображаться в документе параметров интерфейса, чтобы студенты-клиенты знали, какую структуру передать.

В моем каштановом интерфейсе нет правила ответа, потому что мы используем json в качестве возвращаемого носителя, и код ошибки возврата также включен в эту структуру json. И формат json, возвращаемый большинством интерфейсов, очень сложен, а правила ответа swagger в основном не являются описанием метода.
Правила написания ответов swagger основаны на коде ответа http (404, 401 и т. д.). Короче говоря, для нашего интерфейса этот набор правил описания непрост в использовании.
Поэтому я напрямую отказался от описания ответа и напрямую использовал чванство, чтобы сразу же запросить интерфейс и посмотреть, что было возвращено. Если это не сработает, я опишу возвращаемую информацию интерфейс в описании.

После того, как документ будет написан, вы можете вызвать команду swagger-php / bin / swagger для создания swagger.json, а затем скопировать ее в каталог, указанный в swagger-ui, и получить доступ к документу.

NOTE:
Вы должны были это уже видеть. На самом деле, комментарии интерфейса не обязательно должны быть написаны в интерфейсе. Написание комментариев из воздуха также может привести к созданию документации. Так что нет необходимости запутывать где размещается каждый комментарий.Например, swagger Общее определение, определение тега и т. д. можно записать в любой файл php, который можно просканировать.

Краткое описание часто используемых полей:

Вот лишь краткое описание того, как понять и перевести самостоятельно. Для более подробного описания полей мне все равно нужно посмотреть документ. Опубликуйте документ еще раз:
http://bfanger.nl/swagger-explained/#/swaggerObject

Описание интерфейса (@SWG \ Get, @SWG \ Post и т. Д.) Общие поля:

summary - string
 Краткое введение в интерфейс, которое будет отображаться в заголовке интерфейса и не может превышать 120 символов.

description - string
 Подробное знакомство с интерфейсом

externalDocs - string
 Ссылки на внешние документы

operationId - string
 Глобально уникальный идентификатор интерфейса

consumes - [string]
 Тип MIME, полученный интерфейсом

produces - [string]
 Тип MIME, возвращаемый интерфейсом, например application / json

schemes -   [string]
 Протоколы, поддерживаемые интерфейсом, значения ограничены: «http», «https», «ws», «wss».

parameters -    [Parameter Object | Reference Object]
 список параметров

Описание параметра (@SWG \ Parameter) Общие поля:

name - string
 Имя параметра. Есть меры предосторожности при передаче параметров по пути (in принимает значение "path"), бесполезно, лень читать ...

in - string
 Откуда берутся параметры? Обязательно. Допустимые значения: «запрос», «заголовок», «путь», «formData», «тело»

description - string
 Описание параметра. Лучше не быть слишком длинным.

type - string
 Тип параметра. Возможные значения: «строка», «число», «целое число», «логическое», «массив», «файл».

required - boolean
 Является ли параметр обязательным. Это должно быть true при передаче параметра по пути (in принимает значение "path").

default - *
 Значение по умолчанию. Есть много правил, когда вы планируете передавать параметры по пути, я их не использовал. Прочтите документацию сами.

Возникшие проблемы:

Междоменные проблемы:

Место swagger Niu X заключается в том, что он может получить доступ к интерфейсу на месте в документе и отображать вывод, что очень удобно для отладки и интерфейса.Но если вы не хотите развертывать swagger-ui в рамках проекта интерфейса, тогда вы можете получить доступ к интерфейсу на месте в swagger-ui. В случае, если результат не будет запрошен из-за междоменных проблем (заголовки ответа: нет ответа от сервера; тело ответа: нет содержимого).
Я не буду здесь говорить о том, как решать междоменные проблемы, а просто расскажу всем, кто сталкивается с вышеуказанными проблемами, зная, что ошибка вызвана междоменными проблемами, просто решите Это.

Авторизация входа:

Если вы хотите разместить документ в общедоступной сети, нецелесообразно напрямую открывать собственный интерфейс, поэтому адрес доступа к документу должен быть аутентифицирован.
Поскольку требования к безопасности невысоки и в небольшой группе разработчиков компании не так много людей, я напрямую использовал базовую аутентификацию http, предоставляемую nginx, для аутентификации при входе.
Старые правила, первые официальные документы:
https://www.nginx.com/resources/admin-guide/restricting-access-auth-basic/
Эта аутентификация очень проста, она занимает минуту. Не вдаваясь в подробности, всего два шага:

1. Сначала используйте команду htpasswd, чтобы сгенерировать имя учетной записи и пароль для учащихся, которым необходим доступ к документу:

$ htpasswd -cb your/path/to/api_project_accounts.db admin password_for_admin
$ htpasswd -b your/path/to/api_project_accounts.db liuxu 123456
$ htpasswd your/path/to/api_project_accounts.db xiaoming

-c Параметр означает, что если файл учетной записи (api_project_accounts.db) не существует, создайте его. Следовательно, вы должны добавить -c при создании первой учетной записи и не добавлять -c при создании ее впоследствии.
-b Параметр указывает, что пароль указан в виде обычного текста, который является последним вводом в первой и второй командах выше (password_for_admin, 123456). Поэтому, если вы не хотите указывать его в виде обычного текста, вы можете опустить -b. Как и третья команда выше, она будет объединена с sudo. Команда такая же, позволяет вам вводить невидимый пароль.
Приведенная выше команда создает три учетных записи, admin, liuxu, xiaoming, и сохраняет пароли учетных записей в файле api_project_accounts.db.

2. Затем в конфигурации проекта nginx откройте базовую аутентификацию http для вашего собственного адреса доступа.

location /dist {
    auth_basic              "my api project login";
    auth_basic_user_file    your/path/to/api_project_accounts.db;
}

Не забудьте перезапустить nginx после изменения:

nginx -s reload

Устанавливается такая простая аутентификация входа.

NOTE:
Если нетhtpass Эта команда может установить apachehttpd Серверное программное обеспечение,htpass В него входит эта команда:

yum install httpd