Laravel trans_choice не работает после обновления PHP 8.0

#php #laravel #laravel-localization #php-8

#php #laravel #laravel-локализация #php-8

Вопрос:

У меня есть следующее утверждение в тесте функций:

 // just a convenience method to post a CSV file
$this->importData($postdata, $csv)
    ->assertStatus(200)
    ->assertExactJson([
        "alert" => null,
        // response text copied from RoomController::import()
        "message" => sprintf(__("%d items were created or updated."), count($csv_data)),
    ]);
 

В PHP 7.4 это проходит без проблем. Не внося никаких изменений в код моего приложения, я обновился до PHP 8.0, и теперь мне представлен:

   Failed asserting that two strings are equal.
  --- Expected
      Actual
  @@ @@
  -'{"alert":null,"message":"2 items were created or updated."}'
   '{"alert":null,"message":"2 item was created or updated."}'
 

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

 if ($errcount === 0) {
    $response_code = 200;
    $msg = sprintf(
        trans_choice(
            "{0}No items were created or updated.|{1}%d item was created or updated.|{2,}%d items were created or updated.",
            $count
        ),
        $count
    );
} else {
    // some other stuff
}
return response()->json(["message" => $msg, "alert" => $alert], $response_code);
 

Итак, моя проблема в том, что trans_choice по какой-то причине возвращается единственный элемент в PHP 8.0.

Я не могу найти объяснения, почему это может происходить. Возвращаясь к PHP 7.4, все повторяется снова, так что это определенно связано с версией PHP. Устранение неполадок затруднено, потому что, когда я запускаю artisan tinker и делаю echo trans_choice("{0}foo|{1}bar|{2,}baz", 3); , я всегда получаю «bar» в результате, независимо от того, использую ли я PHP 7.4 или 8.0.

Язык не должен входить в это, поскольку я использую необработанные строки, но для записи оба locale и locale_fallback in config/app.php установлены в «en».

Ответ №1:

Хорошо, после множества dump и dd я смог отследить различное поведение IlluminateTranslationMessageSelector::extractFromString() , а также понять, что я использую неправильный синтаксис.

Этот метод выполняет некоторое регулярное выражение, за которым следует нечеткое сравнение между условием и значением. Единственная причина, по которой он работал в PHP 7.4, заключалась в том, что условие «2» свободно равно 2. Сравнение строк с целыми числами выполняется более разумным способом в 8.0, поэтому метод возвращает null во всех случаях, и используется единственное значение по умолчанию.

Однако вместо использования синтаксиса регулярных {2,} выражений я должен был определять свою строку следующим образом:

 "{0}No items were created or updated.|{1}%d item was created or updated.|{2,*}%d items were created or updated."
 

Функция обнаруживает звездочку и замыкает возврат, чтобы дать правильное значение. Если бы я тестировал любое значение, отличное от 0, 1 или 2, мои тесты не прошли бы ни в одной версии PHP.

Комментарии:

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