Skip to content

Commit

Permalink
Merge pull request PrestaShop#36477 from matthieu-rolland/improve-lazy
Browse files Browse the repository at this point in the history
implement php attribute for lazy array + other improvements
  • Loading branch information
matthieu-rolland authored Jul 4, 2024
2 parents cc81f8b + a959124 commit 0899c72
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 57 deletions.
87 changes: 73 additions & 14 deletions src/Adapter/Presenter/AbstractLazyArray.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,6 @@
*/
abstract class AbstractLazyArray implements Iterator, ArrayAccess, Countable, JsonSerializable
{
private const INDEX_NAME_PATTERN = '/@indexName\s+"([^"]+)"/';

/**
* @var ArrayObject
*/
Expand All @@ -96,18 +94,14 @@ public function __construct()
$reflectionClass = new ReflectionClass(static::class);
$methods = $reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC);
foreach ($methods as $method) {
$methodDoc = $method->getDocComment();
if (str_contains($methodDoc, '@arrayAccess')) {
if (preg_match(self::INDEX_NAME_PATTERN, $methodDoc, $matches)) {
$indexName = $matches[1];
} else {
$indexName = $this->convertMethodNameToIndex($method->getName());
}
$attributeInstance = $this->getAttributeInstanceFromMethod($method);
if ($this->isArrayAccessMethod($attributeInstance, $method)) {
$this->arrayAccessList->offsetSet(
$indexName,
$this->getIndexNameFromMethod($attributeInstance, $method),
[
'type' => 'method',
'value' => $method->getName(),
'isRewritable' => $this->isResultRewritable($reflectionClass, $attributeInstance),
]
);
}
Expand Down Expand Up @@ -400,12 +394,20 @@ public function intersectKey($array)
*/
public function offsetSet($offset, $value, $force = false): void
{
if (!$force && $this->arrayAccessList->offsetExists($offset)) {
$result = $this->arrayAccessList->offsetGet($offset);
if ($result['type'] !== 'variable') {
throw new RuntimeException('Trying to set the index ' . print_r($offset, true) . ' of the LazyArray ' . static::class . ' already defined by a method is not allowed');
// verify if the offset exists and is not rewritable, unless forced
if ($this->arrayAccessList->offsetExists($offset)) {
$offsetData = $this->arrayAccessList->offsetGet($offset);

if (!$force && !$offsetData['isRewritable'] && $offsetData['type'] !== 'variable') {
$errorMessage = sprintf(
'Trying to set the index %s of the LazyArray %s already defined by a method is not allowed.',
print_r($offset, true),
static::class
);
throw new RuntimeException($errorMessage);
}
}

$this->arrayAccessList->offsetSet($offset, [
'type' => 'variable',
'value' => $value,
Expand Down Expand Up @@ -440,4 +442,61 @@ private function convertMethodNameToIndex($methodName)

return Inflector::getInflector()->tableize($strippedMethodName);
}

private function isResultRewritable(ReflectionClass $reflexionClass, ?LazyArrayAttribute $methodAttributeInstance): bool
{
if (!is_null($methodAttributeInstance) && !is_null($methodAttributeInstance->isRewritable)) {
return $methodAttributeInstance->isRewritable;
}

// no attribute found at method level, let's check at class level
$classAttributeInstance = null;
$classAttributes = $reflexionClass->getAttributes();

if (!empty($classAttributes)) {
$classAttributeInstance = $classAttributes[0]->newInstance();
if (isset($classAttributeInstance->isRewritable)) {
return $classAttributeInstance->isRewritable;
}
}

return false;
}

private function getAttributeInstanceFromMethod(ReflectionMethod $method): ?LazyArrayAttribute
{
$attributeInstance = null;
$methodAttributes = $method->getAttributes(LazyArrayAttribute::class);

if (!empty($methodAttributes)) {
$attributeInstance = $methodAttributes[0]->newInstance();
}

return $attributeInstance;
}

private function getIndexNameFromMethod(?LazyArrayAttribute $attributeInstance, ReflectionMethod $method): string
{
if (!is_null($attributeInstance) && !empty($attributeInstance->indexName)) {
return $attributeInstance->indexName;
}

return $this->convertMethodNameToIndex($method->getName());
}

private function isArrayAccessMethod($attributeInstance, $method): bool
{
if (!is_null($attributeInstance)) {
return $attributeInstance->arrayAccess;
}

@trigger_error(
'Configuring a method as arrayAccess through annotations is deprecated since version 9.0.0, use php attributes instead, using the LazyArrayAttribute class.',
E_USER_DEPRECATED
);

$methodDoc = $method->getDocComment();

return str_contains($methodDoc, '@arrayAccess');
}
}
58 changes: 15 additions & 43 deletions src/Adapter/Presenter/Cart/CartLazyArray.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
use Link;
use PrestaShop\PrestaShop\Adapter\Image\ImageRetriever;
use PrestaShop\PrestaShop\Adapter\Presenter\AbstractLazyArray;
use PrestaShop\PrestaShop\Adapter\Presenter\LazyArrayAttribute;
use PrestaShop\PrestaShop\Adapter\Presenter\Product\ProductLazyArray;
use PrestaShop\PrestaShop\Adapter\Presenter\Product\ProductListingLazyArray;
use PrestaShop\PrestaShop\Adapter\Presenter\Product\ProductListingPresenter;
Expand All @@ -43,6 +44,7 @@
use Symfony\Contracts\Translation\TranslatorInterface;
use Tools;

#[LazyArrayAttribute(isRewritable: true)]
class CartLazyArray extends AbstractLazyArray
{
private bool $shouldSeparateGifts;
Expand Down Expand Up @@ -98,9 +100,7 @@ public function __construct(Cart $cart, CartPresenter $cartPresenter, bool $shou
parent::__construct();
}

/**
* @arrayAccess
*/
#[LazyArrayAttribute(arrayAccess: true)]
public function getProducts(): array
{
if ($this->shouldSeparateGifts) {
Expand All @@ -115,9 +115,7 @@ public function getProducts(): array
return $this->products;
}

/**
* @arrayAccess
*/
#[LazyArrayAttribute(arrayAccess: true)]
public function getTotals(): array
{
$total_excluding_tax = $this->cart->getOrderTotal(false);
Expand Down Expand Up @@ -149,9 +147,7 @@ public function getTotals(): array
return $this->totals;
}

/**
* @arrayAccess
*/
#[LazyArrayAttribute(arrayAccess: true)]
public function getSubtotals(): array
{
$subtotals = [];
Expand Down Expand Up @@ -218,9 +214,7 @@ public function getSubtotals(): array
return $this->subTotals;
}

/**
* @arrayAccess
*/
#[LazyArrayAttribute(arrayAccess: true)]
public function getProductsCount(): int
{
// If product list is already available, no need to execute a new sql query
Expand All @@ -238,9 +232,7 @@ public function getProductsCount(): int
return $this->productsCount;
}

/**
* @arrayAccess
*/
#[LazyArrayAttribute(arrayAccess: true)]
public function getSummaryString(): string
{
$productsCount = $this->getProductsCount();
Expand All @@ -252,9 +244,7 @@ public function getSummaryString(): string
return $this->summaryString;
}

/**
* @arrayAccess
*/
#[LazyArrayAttribute(arrayAccess: true)]
public function getLabels(): array
{
$this->labels = [
Expand All @@ -269,49 +259,39 @@ public function getLabels(): array
return $this->labels;
}

/**
* @arrayAccess
*/
#[LazyArrayAttribute(arrayAccess: true)]
public function getIdAddressDelivery(): ?int
{
$this->idAddressDelivery = $this->cart->id_address_delivery;

return $this->idAddressDelivery;
}

/**
* @arrayAccess
*/
#[LazyArrayAttribute(arrayAccess: true)]
public function getIdAddressInvoice(): ?int
{
$this->idAddressInvoice = $this->cart->id_address_invoice;

return $this->idAddressInvoice;
}

/**
* @arrayAccess
*/
#[LazyArrayAttribute(arrayAccess: true)]
public function getIsVirtual(): bool
{
$this->isVirtual = $this->cart->isVirtualCart();

return $this->isVirtual;
}

/**
* @arrayAccess
*/
#[LazyArrayAttribute(arrayAccess: true)]
public function getVouchers(): array
{
$this->vouchers = $this->getTemplateVarVouchers();

return $this->vouchers;
}

/**
* @arrayAccess
*/
#[LazyArrayAttribute(arrayAccess: true)]
public function getDiscounts(): array
{
$vouchers = $this->getVouchers();
Expand Down Expand Up @@ -340,11 +320,7 @@ function ($voucher) {
return $this->discounts;
}

/**
* @arrayAccess
*
* @indexName "minimalPurchase"
*/
#[LazyArrayAttribute(arrayAccess: true, indexName: 'minimalPurchase')]
public function getMinimalPurchase(): float
{
$minimalPurchase = $this->priceFormatter->convertAmount((float) Configuration::get('PS_PURCHASE_MINIMUM'));
Expand All @@ -357,11 +333,7 @@ public function getMinimalPurchase(): float
return $this->minimalPurchase;
}

/**
* @arrayAccess
*
* @indexName "minimalPurchaseRequired"
*/
#[LazyArrayAttribute(arrayAccess: true, indexName: 'minimalPurchaseRequired')]
public function getMinimalPurchaseRequired(): string
{
$minimalPurchase = $this->getMinimalPurchase();
Expand Down
40 changes: 40 additions & 0 deletions src/Adapter/Presenter/LazyArrayAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to [email protected] so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <[email protected]>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/

namespace PrestaShop\PrestaShop\Adapter\Presenter;

use Attribute;

#[Attribute]
class LazyArrayAttribute
{
public function __construct(
public bool $arrayAccess = false,
public ?string $indexName = null,
public ?bool $isRewritable = null
) {
}
}

0 comments on commit 0899c72

Please sign in to comment.