diff --git a/config/packages/dev/easy_log_handler.yaml b/config/packages/dev/easy_log_handler.yaml deleted file mode 100644 index 27bfc608..00000000 --- a/config/packages/dev/easy_log_handler.yaml +++ /dev/null @@ -1,16 +0,0 @@ -services: - EasyCorp\EasyLog\EasyLogHandler: - public: false - arguments: ['%kernel.logs_dir%/%kernel.environment%.log'] - -#// FIXME: How to add this configuration automatically without messing up with the monolog configuration? -#monolog: -# handlers: -# buffered: -# type: buffer -# handler: easylog -# channels: ['!event'] -# level: debug -# easylog: -# type: service -# id: EasyCorp\EasyLog\EasyLogHandler diff --git a/config/packages/doctrine.php b/config/packages/doctrine.php index e5be011f..f62cdcfb 100644 --- a/config/packages/doctrine.php +++ b/config/packages/doctrine.php @@ -20,16 +20,16 @@ declare(strict_types=1); -use Symfony\Config\DoctrineConfig; - /** - * This class extends the default doctrine ORM configuration to enable native lazy objects on PHP 8.4+. + * This file enables native lazy objects on PHP 8.4+. * We have to do this in a PHP file, because the yaml file does not support conditionals on PHP version. + * + * TODO: Remove this file when we drop support for PHP < 8.4 */ -return static function(DoctrineConfig $doctrine) { - //On PHP 8.4+ we can use native lazy objects, which are much more efficient than proxies. - if (PHP_VERSION_ID >= 80400) { - $doctrine->orm()->enableNativeLazyObjects(true); - } -}; +// On PHP 8.4+ we can use native lazy objects, which are much more efficient than proxies. +if (PHP_VERSION_ID >= 80400) { + return ['doctrine' => ['orm' => ['enable_native_lazy_objects' => true]]]; +} + +return []; diff --git a/src/Doctrine/Middleware/SQLiteRegexExtensionMiddlewareDriver.php b/src/Doctrine/Middleware/SQLiteRegexExtensionMiddlewareDriver.php index aa6108c9..f721f986 100644 --- a/src/Doctrine/Middleware/SQLiteRegexExtensionMiddlewareDriver.php +++ b/src/Doctrine/Middleware/SQLiteRegexExtensionMiddlewareDriver.php @@ -27,6 +27,7 @@ use App\Doctrine\Functions\SiValueSort; use App\Exceptions\InvalidRegexException; use Doctrine\DBAL\Driver\Connection; use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware; +use Pdo\Sqlite; /** * This middleware is used to add the regexp operator to the SQLite platform. @@ -44,17 +45,30 @@ class SQLiteRegexExtensionMiddlewareDriver extends AbstractDriverMiddleware if ($params['driver'] === 'pdo_sqlite') { $native_connection = $connection->getNativeConnection(); - //Ensure that the function really exists on the connection, as it is marked as experimental according to PHP documentation if($native_connection instanceof \PDO) { - $native_connection->sqliteCreateFunction('REGEXP', self::regexp(...), 2, \PDO::SQLITE_DETERMINISTIC); - $native_connection->sqliteCreateFunction('FIELD', self::field(...), -1, \PDO::SQLITE_DETERMINISTIC); - $native_connection->sqliteCreateFunction('FIELD2', self::field2(...), 2, \PDO::SQLITE_DETERMINISTIC); - //Create a new collation for natural sorting - $native_connection->sqliteCreateCollation('NATURAL_CMP', strnatcmp(...)); + //Use the new PDO::createFunction and PDO::createCollation methods if available (PHP 8.4+) + if (is_a($native_connection, Sqlite::class)) { #TODO: Remove this check when PHP 8.4 is the minimum requirement + $native_connection->createFunction('REGEXP', self::regexp(...), 2, Sqlite::DETERMINISTIC); + $native_connection->createFunction('FIELD', self::field(...), -1, Sqlite::DETERMINISTIC); + $native_connection->createFunction('FIELD2', self::field2(...), 2, Sqlite::DETERMINISTIC); - //Create a function for SI prefix value sorting - $native_connection->sqliteCreateFunction('SI_VALUE', SiValueSort::sqliteSiValue(...), 1, \PDO::SQLITE_DETERMINISTIC); + //Create a new collation for natural sorting + $native_connection->createCollation('NATURAL_CMP', strnatcmp(...)); + + //Create a function for SI prefix value sorting + $native_connection->createFunction('SI_VALUE', SiValueSort::sqliteSiValue(...), 1, Sqlite::DETERMINISTIC); + } else { + $native_connection->sqliteCreateFunction('REGEXP', self::regexp(...), 2, \PDO::SQLITE_DETERMINISTIC); + $native_connection->sqliteCreateFunction('FIELD', self::field(...), -1, \PDO::SQLITE_DETERMINISTIC); + $native_connection->sqliteCreateFunction('FIELD2', self::field2(...), 2, \PDO::SQLITE_DETERMINISTIC); + + //Create a new collation for natural sorting + $native_connection->sqliteCreateCollation('NATURAL_CMP', strnatcmp(...)); + + //Create a function for SI prefix value sorting + $native_connection->sqliteCreateFunction('SI_VALUE', SiValueSort::sqliteSiValue(...), 1, \PDO::SQLITE_DETERMINISTIC); + } } } @@ -118,4 +132,4 @@ class SQLiteRegexExtensionMiddlewareDriver extends AbstractDriverMiddleware return $index + 1; } -} \ No newline at end of file +} diff --git a/src/Entity/ProjectSystem/Project.php b/src/Entity/ProjectSystem/Project.php index a103d694..f4a3e9de 100644 --- a/src/Entity/ProjectSystem/Project.php +++ b/src/Entity/ProjectSystem/Project.php @@ -117,7 +117,7 @@ class Project extends AbstractStructuralDBElement /** * @var string|null The current status of the project */ - #[Assert\Choice(['draft', 'planning', 'in_production', 'finished', 'archived'])] + #[Assert\Choice(choices: ['draft', 'planning', 'in_production', 'finished', 'archived'])] #[Groups(['extended', 'full', 'project:read', 'project:write', 'import'])] #[ORM\Column(type: Types::STRING, length: 64, nullable: true)] protected ?string $status = null; diff --git a/src/Form/Type/AttachmentTypeType.php b/src/Form/Type/AttachmentTypeType.php index 099ed282..95b5a254 100644 --- a/src/Form/Type/AttachmentTypeType.php +++ b/src/Form/Type/AttachmentTypeType.php @@ -38,7 +38,7 @@ class AttachmentTypeType extends AbstractType return StructuralEntityType::class; } - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): void { $resolver->define('attachment_filter_class')->allowedTypes('null', 'string')->default(null); diff --git a/src/Helpers/Projects/ProjectBuildRequest.php b/src/Helpers/Projects/ProjectBuildRequest.php index 430d37b5..d2dfe139 100644 --- a/src/Helpers/Projects/ProjectBuildRequest.php +++ b/src/Helpers/Projects/ProjectBuildRequest.php @@ -84,8 +84,10 @@ final class ProjectBuildRequest $remaining_amount = $this->getNeededAmountForBOMEntry($bom_entry); foreach($this->getPartLotsForBOMEntry($bom_entry) as $lot) { //If the lot has instock use it for the build - $this->withdraw_amounts[$lot->getID()] = min($remaining_amount, $lot->getAmount()); - $remaining_amount -= max(0, $this->withdraw_amounts[$lot->getID()]); + $id = $lot->getID() ?? throw new \RuntimeException("Part lot needs to have an ID!"); + + $this->withdraw_amounts[$id] = min($remaining_amount, $lot->getAmount()); + $remaining_amount -= max(0, $this->withdraw_amounts[$id]); } } } @@ -176,6 +178,10 @@ final class ProjectBuildRequest { $lot_id = $lot instanceof PartLot ? $lot->getID() : $lot; + if ($lot_id === null) { + throw new \InvalidArgumentException('The given lot must have an ID!'); + } + if (! array_key_exists($lot_id, $this->withdraw_amounts)) { throw new \InvalidArgumentException('The given lot is not in the withdraw amounts array!'); } @@ -192,10 +198,12 @@ final class ProjectBuildRequest { if ($lot instanceof PartLot) { $lot_id = $lot->getID(); - } elseif (is_int($lot)) { - $lot_id = $lot; } else { - throw new \InvalidArgumentException('The given lot must be an instance of PartLot or an ID of a PartLot!'); + $lot_id = $lot; + } + + if ($lot_id === null) { + throw new \InvalidArgumentException('The given lot must have an ID!'); } $this->withdraw_amounts[$lot_id] = $amount; @@ -296,7 +304,7 @@ final class ProjectBuildRequest * @param bool $dont_check_quantity * @return $this */ - public function setDontCheckQuantity(bool $dont_check_quantity): ProjectBuildRequest + public function setDontCheckQuantity(bool $dont_check_quantity): self { $this->dont_check_quantity = $dont_check_quantity; return $this; diff --git a/src/Repository/DBElementRepository.php b/src/Repository/DBElementRepository.php index f737d91d..6e13aff7 100644 --- a/src/Repository/DBElementRepository.php +++ b/src/Repository/DBElementRepository.php @@ -158,7 +158,6 @@ class DBElementRepository extends EntityRepository { $reflection = new ReflectionClass($element::class); $property = $reflection->getProperty($field); - $property->setAccessible(true); $property->setValue($element, $new_value); } } diff --git a/src/Services/ImportExportSystem/PartKeeprImporter/PKImportHelperTrait.php b/src/Services/ImportExportSystem/PartKeeprImporter/PKImportHelperTrait.php index 08b1c301..cbfb4ee0 100644 --- a/src/Services/ImportExportSystem/PartKeeprImporter/PKImportHelperTrait.php +++ b/src/Services/ImportExportSystem/PartKeeprImporter/PKImportHelperTrait.php @@ -233,7 +233,6 @@ trait PKImportHelperTrait $reflectionClass = new \ReflectionClass($entity); $property = $reflectionClass->getProperty('addedDate'); - $property->setAccessible(true); $property->setValue($entity, $date); } diff --git a/templates/log_system/details/_extra_collection_element_deleted.html.twig b/templates/log_system/details/_extra_collection_element_deleted.html.twig index 221fae95..1c2ce2f3 100644 --- a/templates/log_system/details/_extra_collection_element_deleted.html.twig +++ b/templates/log_system/details/_extra_collection_element_deleted.html.twig @@ -4,7 +4,7 @@

{% trans %}log.collection_deleted.deleted{% endtrans %}: - {{ entity_type_label(entry.deletedElementClass) }} #{{ entry.deletedElementID }} + {{ type_label(entry.deletedElementClass) }} #{{ entry.deletedElementID }} {% if entry.oldName is not empty %} ({{ entry.oldName }}) {% endif %} @@ -12,4 +12,4 @@

{% trans %}log.collection_deleted.on_collection{% endtrans %}: {{ log_helper.translate_field(entry.collectionName) }} -

\ No newline at end of file +

diff --git a/templates/parts/info/_picture.html.twig b/templates/parts/info/_picture.html.twig index e6aa74b3..db6c59ca 100644 --- a/templates/parts/info/_picture.html.twig +++ b/templates/parts/info/_picture.html.twig @@ -19,7 +19,7 @@ {% endif %} @@ -41,4 +41,4 @@ {% else %} Part main image -{% endif %} \ No newline at end of file +{% endif %} diff --git a/templates/parts/info/show_part_info.html.twig b/templates/parts/info/show_part_info.html.twig index 96b5e209..b36ab047 100644 --- a/templates/parts/info/show_part_info.html.twig +++ b/templates/parts/info/show_part_info.html.twig @@ -22,7 +22,7 @@ ({{ timeTravel | format_datetime('short') }}) {% endif %} {% if part.projectBuildPart %} - ({{ entity_type_label(part.builtProject) }}: {{ part.builtProject.name }}) + ({{ type_label(part.builtProject) }}: {{ part.builtProject.name }}) {% endif %} diff --git a/tests/Doctrine/Functions/SiValueSortTest.php b/tests/Doctrine/Functions/SiValueSortTest.php index dbdd9d28..4ef8a397 100644 --- a/tests/Doctrine/Functions/SiValueSortTest.php +++ b/tests/Doctrine/Functions/SiValueSortTest.php @@ -26,6 +26,7 @@ use App\Doctrine\Functions\SiValueSort; use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\DBAL\Platforms\SQLitePlatform; +use PHPUnit\Framework\Attributes\DataProvider; final class SiValueSortTest extends AbstractDoctrineFunctionTestCase { @@ -71,9 +72,7 @@ final class SiValueSortTest extends AbstractDoctrineFunctionTestCase $this->assertSame('SI_VALUE(part_name)', $sql); } - /** - * @dataProvider sqliteSiValueProvider - */ + #[DataProvider('sqliteSiValueProvider')] public function testSqliteSiValue(?string $input, ?float $expected): void { $result = SiValueSort::sqliteSiValue($input); diff --git a/tests/Entity/Attachments/AttachmentTest.php b/tests/Entity/Attachments/AttachmentTest.php index 9e912b97..bef2df16 100644 --- a/tests/Entity/Attachments/AttachmentTest.php +++ b/tests/Entity/Attachments/AttachmentTest.php @@ -276,7 +276,6 @@ final class AttachmentTest extends TestCase { $reflection = new ReflectionClass($object); $reflection_property = $reflection->getProperty($property); - $reflection_property->setAccessible(true); $reflection_property->setValue($object, $value); } diff --git a/tests/Helpers/Projects/ProjectBuildRequestTest.php b/tests/Helpers/Projects/ProjectBuildRequestTest.php index c1fd1498..3014f762 100644 --- a/tests/Helpers/Projects/ProjectBuildRequestTest.php +++ b/tests/Helpers/Projects/ProjectBuildRequestTest.php @@ -82,7 +82,12 @@ final class ProjectBuildRequestTest extends TestCase $part2->setName('Part 2'); $part2->setPartUnit($float_unit); - $this->lot2 = new PartLot(); + $this->lot2 = new class extends PartLot { + public function getID(): ?int + { + return 3; + } + };; $part2->addPartLot($this->lot2); $this->lot2->setAmount(2.5); $this->lot2->setDescription('Lot 2'); diff --git a/tests/Services/InfoProviderSystem/Providers/BuerklinProviderTest.php b/tests/Services/InfoProviderSystem/Providers/BuerklinProviderTest.php index ef446c9a..d466f048 100644 --- a/tests/Services/InfoProviderSystem/Providers/BuerklinProviderTest.php +++ b/tests/Services/InfoProviderSystem/Providers/BuerklinProviderTest.php @@ -77,7 +77,6 @@ final class BuerklinProviderTest extends TestCase public function testAttributesToParametersParsesUnitsAndValues(): void { $method = new \ReflectionMethod(BuerklinProvider::class, 'attributesToParameters'); - $method->setAccessible(true); $features = [ [ @@ -127,7 +126,6 @@ final class BuerklinProviderTest extends TestCase public function testComplianceParameters(): void { $method = new \ReflectionMethod(BuerklinProvider::class, 'complianceToParameters'); - $method->setAccessible(true); $product = [ 'labelRoHS' => 'Yes', @@ -158,7 +156,6 @@ final class BuerklinProviderTest extends TestCase public function testImageSelectionPrefersZoomAndDeduplicates(): void { $method = new \ReflectionMethod(BuerklinProvider::class, 'getProductImages'); - $method->setAccessible(true); $images = [ ['format' => 'product', 'url' => '/img/a.webp'], @@ -176,7 +173,6 @@ final class BuerklinProviderTest extends TestCase public function testFootprintExtraction(): void { $method = new \ReflectionMethod(BuerklinProvider::class, 'getPartDetail'); - $method->setAccessible(true); $product = [ 'code' => 'TEST1', @@ -212,7 +208,6 @@ final class BuerklinProviderTest extends TestCase ]; $method = new \ReflectionMethod(BuerklinProvider::class, 'pricesToVendorInfo'); - $method->setAccessible(true); $vendorInfo = $method->invoke($this->provider, 'SKU1', 'https://x', $detailPrice); @@ -260,7 +255,6 @@ final class BuerklinProviderTest extends TestCase ); $method = new \ReflectionMethod(BuerklinProvider::class, 'convertPartDetailToSearchResult'); - $method->setAccessible(true); $dto = $method->invoke($this->provider, $detail); @@ -273,13 +267,13 @@ final class BuerklinProviderTest extends TestCase { $this->assertSame(['buerklin.com'], $this->provider->getHandledDomains()); } - + #[DataProvider('buerklinIdFromUrlProvider')] public function testGetIDFromURLExtractsId(string $url, ?string $expected): void { $this->assertSame($expected, $this->provider->getIDFromURL($url)); } - + public static function buerklinIdFromUrlProvider(): \Iterator { yield 'de long path' => [ diff --git a/tests/Services/InfoProviderSystem/Providers/LCSCProviderTest.php b/tests/Services/InfoProviderSystem/Providers/LCSCProviderTest.php index dc19de6b..9ec3abe1 100644 --- a/tests/Services/InfoProviderSystem/Providers/LCSCProviderTest.php +++ b/tests/Services/InfoProviderSystem/Providers/LCSCProviderTest.php @@ -367,7 +367,6 @@ final class LCSCProviderTest extends TestCase { $reflection = new \ReflectionClass($this->provider); $method = $reflection->getMethod('sanitizeField'); - $method->setAccessible(true); $this->assertNull($method->invokeArgs($this->provider, [null])); $this->assertEquals('Clean text', $method->invokeArgs($this->provider, ['Clean text'])); @@ -378,7 +377,6 @@ final class LCSCProviderTest extends TestCase { $reflection = new \ReflectionClass($this->provider); $method = $reflection->getMethod('getUsedCurrency'); - $method->setAccessible(true); $this->assertEquals('USD', $method->invokeArgs($this->provider, ['US$'])); $this->assertEquals('USD', $method->invokeArgs($this->provider, ['$'])); @@ -391,7 +389,6 @@ final class LCSCProviderTest extends TestCase { $reflection = new \ReflectionClass($this->provider); $method = $reflection->getMethod('getProductShortURL'); - $method->setAccessible(true); $result = $method->invokeArgs($this->provider, ['C123456']); $this->assertEquals('https://www.lcsc.com/product-detail/C123456.html', $result); @@ -401,7 +398,6 @@ final class LCSCProviderTest extends TestCase { $reflection = new \ReflectionClass($this->provider); $method = $reflection->getMethod('getProductDatasheets'); - $method->setAccessible(true); $result = $method->invokeArgs($this->provider, [null]); $this->assertIsArray($result); @@ -417,7 +413,6 @@ final class LCSCProviderTest extends TestCase { $reflection = new \ReflectionClass($this->provider); $method = $reflection->getMethod('getProductImages'); - $method->setAccessible(true); $result = $method->invokeArgs($this->provider, [null]); $this->assertIsArray($result); @@ -434,7 +429,6 @@ final class LCSCProviderTest extends TestCase { $reflection = new \ReflectionClass($this->provider); $method = $reflection->getMethod('attributesToParameters'); - $method->setAccessible(true); $attributes = [ ['paramNameEn' => 'Resistance', 'paramValueEn' => '1kΩ'], @@ -454,7 +448,6 @@ final class LCSCProviderTest extends TestCase { $reflection = new \ReflectionClass($this->provider); $method = $reflection->getMethod('pricesToVendorInfo'); - $method->setAccessible(true); $prices = [ ['ladder' => 1, 'productPrice' => '0.10', 'currencySymbol' => 'US$'],