Compare commits

..

9 commits

Author SHA1 Message Date
Jan Böhmer
188444b30f Fixed deprecations
Some checks are pending
Build assets artifact / Build assets artifact (push) Waiting to run
Docker Image Build / build (linux/amd64, amd64, ubuntu-latest) (push) Waiting to run
Docker Image Build / build (linux/arm/v7, armv7, ubuntu-24.04-arm) (push) Waiting to run
Docker Image Build / build (linux/arm64, arm64, ubuntu-24.04-arm) (push) Waiting to run
Docker Image Build / merge (push) Blocked by required conditions
Docker Image Build (FrankenPHP) / build (linux/amd64, amd64, ubuntu-latest) (push) Waiting to run
Docker Image Build (FrankenPHP) / build (linux/arm/v7, armv7, ubuntu-24.04-arm) (push) Waiting to run
Docker Image Build (FrankenPHP) / build (linux/arm64, arm64, ubuntu-24.04-arm) (push) Waiting to run
Docker Image Build (FrankenPHP) / merge (push) Blocked by required conditions
Static analysis / Static analysis (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.2, mysql) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.3, mysql) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.4, mysql) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.5, mysql) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.2, postgres) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.3, postgres) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.4, postgres) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.5, postgres) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.2, sqlite) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.3, sqlite) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.4, sqlite) (push) Waiting to run
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.5, sqlite) (push) Waiting to run
2026-06-25 12:44:06 +02:00
Jan Böhmer
cd87b59c15 Fixed small deprecations 2026-06-25 12:37:22 +02:00
Jan Böhmer
b3895c1e91 Fixed depprecation of fluent config builders 2026-06-25 12:33:52 +02:00
Jan Böhmer
23e22b19e2 Removed usesless easy_log_handler file 2026-06-25 12:27:37 +02:00
Jan Böhmer
9c919a9be9 Moved from phpunit annotation to attribute 2026-06-25 12:12:34 +02:00
Jan Böhmer
eb7da91c44 Fixed phpstan issue 2026-06-25 12:09:06 +02:00
Jan Böhmer
b8fc5d4ace Warn if ProjectBuild passed lot if null
Fixed caused deprecations for it
2026-06-25 12:07:54 +02:00
Jan Böhmer
fe0809230b Fixed sqlite deprecation on PHP8.5 2026-06-25 11:59:37 +02:00
Jan Böhmer
9d4dabbd20 Removed useless setAccessible() calls
They are noop since 8.1 and we only support 8.2+
2026-06-25 10:54:13 +02:00
16 changed files with 63 additions and 69 deletions

View file

@ -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

View file

@ -20,16 +20,16 @@
declare(strict_types=1); 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. * 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.
//On PHP 8.4+ we can use native lazy objects, which are much more efficient than proxies. if (PHP_VERSION_ID >= 80400) {
if (PHP_VERSION_ID >= 80400) { return ['doctrine' => ['orm' => ['enable_native_lazy_objects' => true]]];
$doctrine->orm()->enableNativeLazyObjects(true); }
}
}; return [];

View file

@ -27,6 +27,7 @@ use App\Doctrine\Functions\SiValueSort;
use App\Exceptions\InvalidRegexException; use App\Exceptions\InvalidRegexException;
use Doctrine\DBAL\Driver\Connection; use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware; use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware;
use Pdo\Sqlite;
/** /**
* This middleware is used to add the regexp operator to the SQLite platform. * This middleware is used to add the regexp operator to the SQLite platform.
@ -44,8 +45,20 @@ class SQLiteRegexExtensionMiddlewareDriver extends AbstractDriverMiddleware
if ($params['driver'] === 'pdo_sqlite') { if ($params['driver'] === 'pdo_sqlite') {
$native_connection = $connection->getNativeConnection(); $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) { if($native_connection instanceof \PDO) {
//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 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('REGEXP', self::regexp(...), 2, \PDO::SQLITE_DETERMINISTIC);
$native_connection->sqliteCreateFunction('FIELD', self::field(...), -1, \PDO::SQLITE_DETERMINISTIC); $native_connection->sqliteCreateFunction('FIELD', self::field(...), -1, \PDO::SQLITE_DETERMINISTIC);
$native_connection->sqliteCreateFunction('FIELD2', self::field2(...), 2, \PDO::SQLITE_DETERMINISTIC); $native_connection->sqliteCreateFunction('FIELD2', self::field2(...), 2, \PDO::SQLITE_DETERMINISTIC);
@ -57,6 +70,7 @@ class SQLiteRegexExtensionMiddlewareDriver extends AbstractDriverMiddleware
$native_connection->sqliteCreateFunction('SI_VALUE', SiValueSort::sqliteSiValue(...), 1, \PDO::SQLITE_DETERMINISTIC); $native_connection->sqliteCreateFunction('SI_VALUE', SiValueSort::sqliteSiValue(...), 1, \PDO::SQLITE_DETERMINISTIC);
} }
} }
}
return $connection; return $connection;

View file

@ -117,7 +117,7 @@ class Project extends AbstractStructuralDBElement
/** /**
* @var string|null The current status of the project * @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'])] #[Groups(['extended', 'full', 'project:read', 'project:write', 'import'])]
#[ORM\Column(type: Types::STRING, length: 64, nullable: true)] #[ORM\Column(type: Types::STRING, length: 64, nullable: true)]
protected ?string $status = null; protected ?string $status = null;

View file

@ -38,7 +38,7 @@ class AttachmentTypeType extends AbstractType
return StructuralEntityType::class; return StructuralEntityType::class;
} }
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver): void
{ {
$resolver->define('attachment_filter_class')->allowedTypes('null', 'string')->default(null); $resolver->define('attachment_filter_class')->allowedTypes('null', 'string')->default(null);

View file

@ -84,8 +84,10 @@ final class ProjectBuildRequest
$remaining_amount = $this->getNeededAmountForBOMEntry($bom_entry); $remaining_amount = $this->getNeededAmountForBOMEntry($bom_entry);
foreach($this->getPartLotsForBOMEntry($bom_entry) as $lot) { foreach($this->getPartLotsForBOMEntry($bom_entry) as $lot) {
//If the lot has instock use it for the build //If the lot has instock use it for the build
$this->withdraw_amounts[$lot->getID()] = min($remaining_amount, $lot->getAmount()); $id = $lot->getID() ?? throw new \RuntimeException("Part lot needs to have an ID!");
$remaining_amount -= max(0, $this->withdraw_amounts[$lot->getID()]);
$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; $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)) { if (! array_key_exists($lot_id, $this->withdraw_amounts)) {
throw new \InvalidArgumentException('The given lot is not in the withdraw amounts array!'); throw new \InvalidArgumentException('The given lot is not in the withdraw amounts array!');
} }
@ -192,10 +198,12 @@ final class ProjectBuildRequest
{ {
if ($lot instanceof PartLot) { if ($lot instanceof PartLot) {
$lot_id = $lot->getID(); $lot_id = $lot->getID();
} elseif (is_int($lot)) {
$lot_id = $lot;
} else { } 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; $this->withdraw_amounts[$lot_id] = $amount;
@ -296,7 +304,7 @@ final class ProjectBuildRequest
* @param bool $dont_check_quantity * @param bool $dont_check_quantity
* @return $this * @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; $this->dont_check_quantity = $dont_check_quantity;
return $this; return $this;

View file

@ -158,7 +158,6 @@ class DBElementRepository extends EntityRepository
{ {
$reflection = new ReflectionClass($element::class); $reflection = new ReflectionClass($element::class);
$property = $reflection->getProperty($field); $property = $reflection->getProperty($field);
$property->setAccessible(true);
$property->setValue($element, $new_value); $property->setValue($element, $new_value);
} }
} }

View file

@ -233,7 +233,6 @@ trait PKImportHelperTrait
$reflectionClass = new \ReflectionClass($entity); $reflectionClass = new \ReflectionClass($entity);
$property = $reflectionClass->getProperty('addedDate'); $property = $reflectionClass->getProperty('addedDate');
$property->setAccessible(true);
$property->setValue($entity, $date); $property->setValue($entity, $date);
} }

View file

@ -4,7 +4,7 @@
<p class="m-0"> <p class="m-0">
<b>{% trans %}log.collection_deleted.deleted{% endtrans %}</b>: <b>{% trans %}log.collection_deleted.deleted{% endtrans %}</b>:
{{ entity_type_label(entry.deletedElementClass) }} #{{ entry.deletedElementID }} {{ type_label(entry.deletedElementClass) }} #{{ entry.deletedElementID }}
{% if entry.oldName is not empty %} {% if entry.oldName is not empty %}
({{ entry.oldName }}) ({{ entry.oldName }})
{% endif %} {% endif %}

View file

@ -19,7 +19,7 @@
<div class="carousel-caption text-white"> <div class="carousel-caption text-white">
<div><b>{{ pic.name }}</b></div> <div><b>{{ pic.name }}</b></div>
<div>{% if pic.filename %}({{ pic.filename }}) {% endif %}</div> <div>{% if pic.filename %}({{ pic.filename }}) {% endif %}</div>
<div>{{ entity_type_label(pic.element) }}</div> <div>{{ type_label(pic.element) }}</div>
</div> </div>
</div> </div>
{% endif %} {% endif %}

View file

@ -22,7 +22,7 @@
<i>({{ timeTravel | format_datetime('short') }})</i> <i>({{ timeTravel | format_datetime('short') }})</i>
{% endif %} {% endif %}
{% if part.projectBuildPart %} {% if part.projectBuildPart %}
(<i>{{ entity_type_label(part.builtProject) }}</i>: <a class="text-white" href="{{ entity_url(part.builtProject) }}">{{ part.builtProject.name }}</a>) (<i>{{ type_label(part.builtProject) }}</i>: <a class="text-white" href="{{ entity_url(part.builtProject) }}">{{ part.builtProject.name }}</a>)
{% endif %} {% endif %}
</span> </span>
<span class="float-end"> <span class="float-end">

View file

@ -26,6 +26,7 @@ use App\Doctrine\Functions\SiValueSort;
use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\MySQLPlatform;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\DBAL\Platforms\SQLitePlatform;
use PHPUnit\Framework\Attributes\DataProvider;
final class SiValueSortTest extends AbstractDoctrineFunctionTestCase final class SiValueSortTest extends AbstractDoctrineFunctionTestCase
{ {
@ -71,9 +72,7 @@ final class SiValueSortTest extends AbstractDoctrineFunctionTestCase
$this->assertSame('SI_VALUE(part_name)', $sql); $this->assertSame('SI_VALUE(part_name)', $sql);
} }
/** #[DataProvider('sqliteSiValueProvider')]
* @dataProvider sqliteSiValueProvider
*/
public function testSqliteSiValue(?string $input, ?float $expected): void public function testSqliteSiValue(?string $input, ?float $expected): void
{ {
$result = SiValueSort::sqliteSiValue($input); $result = SiValueSort::sqliteSiValue($input);

View file

@ -276,7 +276,6 @@ final class AttachmentTest extends TestCase
{ {
$reflection = new ReflectionClass($object); $reflection = new ReflectionClass($object);
$reflection_property = $reflection->getProperty($property); $reflection_property = $reflection->getProperty($property);
$reflection_property->setAccessible(true);
$reflection_property->setValue($object, $value); $reflection_property->setValue($object, $value);
} }

View file

@ -82,7 +82,12 @@ final class ProjectBuildRequestTest extends TestCase
$part2->setName('Part 2'); $part2->setName('Part 2');
$part2->setPartUnit($float_unit); $part2->setPartUnit($float_unit);
$this->lot2 = new PartLot(); $this->lot2 = new class extends PartLot {
public function getID(): ?int
{
return 3;
}
};;
$part2->addPartLot($this->lot2); $part2->addPartLot($this->lot2);
$this->lot2->setAmount(2.5); $this->lot2->setAmount(2.5);
$this->lot2->setDescription('Lot 2'); $this->lot2->setDescription('Lot 2');

View file

@ -77,7 +77,6 @@ final class BuerklinProviderTest extends TestCase
public function testAttributesToParametersParsesUnitsAndValues(): void public function testAttributesToParametersParsesUnitsAndValues(): void
{ {
$method = new \ReflectionMethod(BuerklinProvider::class, 'attributesToParameters'); $method = new \ReflectionMethod(BuerklinProvider::class, 'attributesToParameters');
$method->setAccessible(true);
$features = [ $features = [
[ [
@ -127,7 +126,6 @@ final class BuerklinProviderTest extends TestCase
public function testComplianceParameters(): void public function testComplianceParameters(): void
{ {
$method = new \ReflectionMethod(BuerklinProvider::class, 'complianceToParameters'); $method = new \ReflectionMethod(BuerklinProvider::class, 'complianceToParameters');
$method->setAccessible(true);
$product = [ $product = [
'labelRoHS' => 'Yes', 'labelRoHS' => 'Yes',
@ -158,7 +156,6 @@ final class BuerklinProviderTest extends TestCase
public function testImageSelectionPrefersZoomAndDeduplicates(): void public function testImageSelectionPrefersZoomAndDeduplicates(): void
{ {
$method = new \ReflectionMethod(BuerklinProvider::class, 'getProductImages'); $method = new \ReflectionMethod(BuerklinProvider::class, 'getProductImages');
$method->setAccessible(true);
$images = [ $images = [
['format' => 'product', 'url' => '/img/a.webp'], ['format' => 'product', 'url' => '/img/a.webp'],
@ -176,7 +173,6 @@ final class BuerklinProviderTest extends TestCase
public function testFootprintExtraction(): void public function testFootprintExtraction(): void
{ {
$method = new \ReflectionMethod(BuerklinProvider::class, 'getPartDetail'); $method = new \ReflectionMethod(BuerklinProvider::class, 'getPartDetail');
$method->setAccessible(true);
$product = [ $product = [
'code' => 'TEST1', 'code' => 'TEST1',
@ -212,7 +208,6 @@ final class BuerklinProviderTest extends TestCase
]; ];
$method = new \ReflectionMethod(BuerklinProvider::class, 'pricesToVendorInfo'); $method = new \ReflectionMethod(BuerklinProvider::class, 'pricesToVendorInfo');
$method->setAccessible(true);
$vendorInfo = $method->invoke($this->provider, 'SKU1', 'https://x', $detailPrice); $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 = new \ReflectionMethod(BuerklinProvider::class, 'convertPartDetailToSearchResult');
$method->setAccessible(true);
$dto = $method->invoke($this->provider, $detail); $dto = $method->invoke($this->provider, $detail);

View file

@ -367,7 +367,6 @@ final class LCSCProviderTest extends TestCase
{ {
$reflection = new \ReflectionClass($this->provider); $reflection = new \ReflectionClass($this->provider);
$method = $reflection->getMethod('sanitizeField'); $method = $reflection->getMethod('sanitizeField');
$method->setAccessible(true);
$this->assertNull($method->invokeArgs($this->provider, [null])); $this->assertNull($method->invokeArgs($this->provider, [null]));
$this->assertEquals('Clean text', $method->invokeArgs($this->provider, ['Clean text'])); $this->assertEquals('Clean text', $method->invokeArgs($this->provider, ['Clean text']));
@ -378,7 +377,6 @@ final class LCSCProviderTest extends TestCase
{ {
$reflection = new \ReflectionClass($this->provider); $reflection = new \ReflectionClass($this->provider);
$method = $reflection->getMethod('getUsedCurrency'); $method = $reflection->getMethod('getUsedCurrency');
$method->setAccessible(true);
$this->assertEquals('USD', $method->invokeArgs($this->provider, ['US$'])); $this->assertEquals('USD', $method->invokeArgs($this->provider, ['US$']));
$this->assertEquals('USD', $method->invokeArgs($this->provider, ['$'])); $this->assertEquals('USD', $method->invokeArgs($this->provider, ['$']));
@ -391,7 +389,6 @@ final class LCSCProviderTest extends TestCase
{ {
$reflection = new \ReflectionClass($this->provider); $reflection = new \ReflectionClass($this->provider);
$method = $reflection->getMethod('getProductShortURL'); $method = $reflection->getMethod('getProductShortURL');
$method->setAccessible(true);
$result = $method->invokeArgs($this->provider, ['C123456']); $result = $method->invokeArgs($this->provider, ['C123456']);
$this->assertEquals('https://www.lcsc.com/product-detail/C123456.html', $result); $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); $reflection = new \ReflectionClass($this->provider);
$method = $reflection->getMethod('getProductDatasheets'); $method = $reflection->getMethod('getProductDatasheets');
$method->setAccessible(true);
$result = $method->invokeArgs($this->provider, [null]); $result = $method->invokeArgs($this->provider, [null]);
$this->assertIsArray($result); $this->assertIsArray($result);
@ -417,7 +413,6 @@ final class LCSCProviderTest extends TestCase
{ {
$reflection = new \ReflectionClass($this->provider); $reflection = new \ReflectionClass($this->provider);
$method = $reflection->getMethod('getProductImages'); $method = $reflection->getMethod('getProductImages');
$method->setAccessible(true);
$result = $method->invokeArgs($this->provider, [null]); $result = $method->invokeArgs($this->provider, [null]);
$this->assertIsArray($result); $this->assertIsArray($result);
@ -434,7 +429,6 @@ final class LCSCProviderTest extends TestCase
{ {
$reflection = new \ReflectionClass($this->provider); $reflection = new \ReflectionClass($this->provider);
$method = $reflection->getMethod('attributesToParameters'); $method = $reflection->getMethod('attributesToParameters');
$method->setAccessible(true);
$attributes = [ $attributes = [
['paramNameEn' => 'Resistance', 'paramValueEn' => '1kΩ'], ['paramNameEn' => 'Resistance', 'paramValueEn' => '1kΩ'],
@ -454,7 +448,6 @@ final class LCSCProviderTest extends TestCase
{ {
$reflection = new \ReflectionClass($this->provider); $reflection = new \ReflectionClass($this->provider);
$method = $reflection->getMethod('pricesToVendorInfo'); $method = $reflection->getMethod('pricesToVendorInfo');
$method->setAccessible(true);
$prices = [ $prices = [
['ladder' => 1, 'productPrice' => '0.10', 'currencySymbol' => 'US$'], ['ladder' => 1, 'productPrice' => '0.10', 'currencySymbol' => 'US$'],