- <?php
- namespace Knp\Component\Pager\Event\Subscriber\Sortable;
- use Knp\Component\Pager\Event\ItemsEvent;
- use Knp\Component\Pager\PaginatorInterface;
- use Symfony\Component\EventDispatcher\EventSubscriberInterface;
- use Symfony\Component\HttpFoundation\Request;
- use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
- use Symfony\Component\PropertyAccess\PropertyAccess;
- use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
- class ArraySubscriber implements EventSubscriberInterface
- {
-     /**
-      * @var string the field used to sort current object array list
-      */
-     private string $currentSortingField;
-     /**
-      * @var string the sorting direction
-      */
-     private string $sortDirection;
-     private ?PropertyAccessorInterface $propertyAccessor;
-     private Request $request;
-     public function __construct(Request $request = null, PropertyAccessorInterface $accessor = null)
-     {
-         if (!$accessor && class_exists(PropertyAccess::class)) {
-             $accessor = PropertyAccess::createPropertyAccessorBuilder()->enableMagicCall()->getPropertyAccessor();
-         }
-         $this->propertyAccessor = $accessor;
-         // check needed because $request must be nullable, being the second parameter (with the first one nullable)
-         if (null === $request) {
-             throw new \InvalidArgumentException('Request must be initialized.');
-         }
-         $this->request = $request;
-     }
-     public function items(ItemsEvent $event): void
-     {
-         // Check if the result has already been sorted by an other sort subscriber
-         $customPaginationParameters = $event->getCustomPaginationParameters();
-         if (!empty($customPaginationParameters['sorted']) ) {
-             return;
-         }
-         $sortField = $event->options[PaginatorInterface::SORT_FIELD_PARAMETER_NAME];
-         if (!is_array($event->target) || null === $sortField || !$this->request->query->has($sortField)) {
-             return;
-         }
-         $event->setCustomPaginationParameter('sorted', true);
-         if (isset($event->options[PaginatorInterface::SORT_FIELD_ALLOW_LIST]) && !in_array($this->request->query->get($sortField), $event->options[PaginatorInterface::SORT_FIELD_ALLOW_LIST])) {
-             throw new \UnexpectedValueException("Cannot sort by: [{$this->request->query->get($sortField)}] this field is not in allow list.");
-         }
-         $sortFunction = $event->options['sortFunction'] ?? [$this, 'proxySortFunction'];
-         $sortField = $this->request->query->get($sortField);
-         // compatibility layer
-         if ($sortField[0] === '.') {
-             $sortField = substr($sortField, 1);
-         }
-         call_user_func_array($sortFunction, [
-             &$event->target,
-             $sortField,
-             $this->getSortDirection($event->options),
-         ]);
-     }
-     private function getSortDirection(array $options): string
-     {
-         if (!$this->request->query->has($options[PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME])) {
-             return 'desc';
-         }
-         $direction = $this->request->query->get($options[PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME]);
-         if (strtolower($direction) === 'asc') {
-             return 'asc';
-         }
-         return 'desc';
-     }
-     private function proxySortFunction(&$target, $sortField, $sortDirection): bool
-     {
-         $this->currentSortingField = $sortField;
-         $this->sortDirection = $sortDirection;
-         return usort($target, [$this, 'sortFunction']);
-     }
-     /**
-      * @param mixed $object1 first object to compare
-      * @param mixed $object2 second object to compare
-      *
-      * @return int
-      */
-     private function sortFunction($object1, $object2): int
-     {
-         if (null === $this->propertyAccessor) {
-             throw new \UnexpectedValueException('You need symfony/property-access component to use this sorting function');
-         }
-         if (!$this->propertyAccessor->isReadable($object1, $this->currentSortingField) || !$this->propertyAccessor->isReadable($object2, $this->currentSortingField)) {
-             return 0;
-         }
-         try {
-             $fieldValue1 = $this->propertyAccessor->getValue($object1, $this->currentSortingField);
-         } catch (UnexpectedTypeException $e) {
-             return -1 * $this->getSortCoefficient();
-         }
-         try {
-             $fieldValue2 = $this->propertyAccessor->getValue($object2, $this->currentSortingField);
-         } catch (UnexpectedTypeException $e) {
-             return 1 * $this->getSortCoefficient();
-         }
-         if (is_string($fieldValue1)) {
-             $fieldValue1 = mb_strtolower($fieldValue1);
-         }
-         if (is_string($fieldValue2)) {
-             $fieldValue2 = mb_strtolower($fieldValue2);
-         }
-         if ($fieldValue1 === $fieldValue2) {
-             return 0;
-         }
-         return ($fieldValue1 > $fieldValue2 ? 1 : -1) * $this->getSortCoefficient();
-     }
-     private function getSortCoefficient(): int
-     {
-         return $this->sortDirection === 'asc' ? 1 : -1;
-     }
-     public static function getSubscribedEvents(): array
-     {
-         return [
-             'knp_pager.items' => ['items', 1],
-         ];
-     }
- }
-