Fixed phpstan analysis issues and bad code that showed up with phpstan 2.0

This commit is contained in:
Jan Böhmer 2024-12-28 22:31:04 +01:00
parent a273acbecd
commit 946032a101
42 changed files with 98 additions and 85 deletions

View file

@ -57,4 +57,7 @@ parameters:
- '#Part::getParameters\(\) should return .*AbstractParameter#' - '#Part::getParameters\(\) should return .*AbstractParameter#'
# Ignore doctrine type mapping mismatch # Ignore doctrine type mapping mismatch
- '#Property .* type mapping mismatch: property can contain .* but database expects .*#' - '#Property .* type mapping mismatch: property can contain .* but database expects .*#'
# Ignore error of unused WithPermPresetsTrait, as it is used in the migrations which are not analyzed by Phpstan
- '#Trait App\\Migration\\WithPermPresetsTrait is used zero times and is not analysed#'

View file

@ -79,7 +79,7 @@ class CheckRequirementsCommand extends Command
//Checking 32-bit system //Checking 32-bit system
if (PHP_INT_SIZE === 4) { if (PHP_INT_SIZE === 4) {
$io->warning('You are using a 32-bit system. You will have problems with working with dates after the year 2038, therefore a 64-bit system is recommended.'); $io->warning('You are using a 32-bit system. You will have problems with working with dates after the year 2038, therefore a 64-bit system is recommended.');
} elseif (PHP_INT_SIZE === 8) { } elseif (PHP_INT_SIZE === 8) { //@phpstan-ignore-line //PHP_INT_SIZE is always 4 or 8
if (!$only_issues) { if (!$only_issues) {
$io->success('You are using a 64-bit system.'); $io->success('You are using a 64-bit system.');
} }

View file

@ -79,6 +79,7 @@ class ConvertBBCodeCommand extends Command
/** /**
* Returns a list which entities and which properties need to be checked. * Returns a list which entities and which properties need to be checked.
* @return array<class-string<AbstractNamedDBElement>, string[]>
*/ */
protected function getTargetsLists(): array protected function getTargetsLists(): array
{ {
@ -109,7 +110,6 @@ class ConvertBBCodeCommand extends Command
$class $class
)); ));
//Determine which entities of this type we need to modify //Determine which entities of this type we need to modify
/** @var EntityRepository $repo */
$repo = $this->em->getRepository($class); $repo = $this->em->getRepository($class);
$qb = $repo->createQueryBuilder('e') $qb = $repo->createQueryBuilder('e')
->select('e'); ->select('e');

View file

@ -206,12 +206,15 @@ class UsersPermissionsCommand extends Command
return '<fg=green>Allow</>'; return '<fg=green>Allow</>';
} elseif ($permission_value === false) { } elseif ($permission_value === false) {
return '<fg=red>Disallow</>'; return '<fg=red>Disallow</>';
} elseif ($permission_value === null && !$inherit) { }
// Permission value is null by this point
elseif (!$inherit) {
return '<fg=blue>Inherit</>'; return '<fg=blue>Inherit</>';
} elseif ($permission_value === null && $inherit) { } elseif ($inherit) {
return '<fg=red>Disallow (Inherited)</>'; return '<fg=red>Disallow (Inherited)</>';
} }
//@phpstan-ignore-next-line This line is never reached, but PHPstorm complains otherwise
return '???'; return '???';
} }
} }

View file

@ -221,7 +221,6 @@ abstract class BaseAdminController extends AbstractController
} }
} }
/** @var AbstractPartsContainingRepository $repo */
$repo = $this->entityManager->getRepository($this->entity_class); $repo = $this->entityManager->getRepository($this->entity_class);
return $this->render($this->twig_template, [ return $this->render($this->twig_template, [
@ -397,7 +396,7 @@ abstract class BaseAdminController extends AbstractController
{ {
if ($entity instanceof AbstractPartsContainingDBElement) { if ($entity instanceof AbstractPartsContainingDBElement) {
/** @var AbstractPartsContainingRepository $repo */ /** @var AbstractPartsContainingRepository $repo */
$repo = $this->entityManager->getRepository($this->entity_class); $repo = $this->entityManager->getRepository($this->entity_class); //@phpstan-ignore-line
if ($repo->getPartsCount($entity) > 0) { if ($repo->getPartsCount($entity) > 0) {
$this->addFlash('error', t('entity.delete.must_not_contain_parts', ['%PATH%' => $entity->getFullPath()])); $this->addFlash('error', t('entity.delete.must_not_contain_parts', ['%PATH%' => $entity->getFullPath()]));

View file

@ -152,7 +152,7 @@ class LabelController extends AbstractController
{ {
$id_array = $this->rangeParser->parse($ids); $id_array = $this->rangeParser->parse($ids);
/** @var DBElementRepository $repo */ /** @var DBElementRepository<AbstractDBElement> $repo */
$repo = $this->em->getRepository($type->getEntityClass()); $repo = $this->em->getRepository($type->getEntityClass());
return $repo->getElementsFromIDArray($id_array); return $repo->getElementsFromIDArray($id_array);

View file

@ -60,6 +60,7 @@ class PartListsController extends AbstractController
$ids = $request->request->get('ids'); $ids = $request->request->get('ids');
$action = $request->request->get('action'); $action = $request->request->get('action');
$target = $request->request->get('target'); $target = $request->request->get('target');
$redirectResponse = null;
if (!$this->isCsrfTokenValid('table_action', $request->request->get('_token'))) { if (!$this->isCsrfTokenValid('table_action', $request->request->get('_token'))) {
$this->addFlash('error', 'csfr_invalid'); $this->addFlash('error', 'csfr_invalid');
@ -80,7 +81,7 @@ class PartListsController extends AbstractController
} }
//If the action handler returned a response, we use it, otherwise we redirect back to the previous page. //If the action handler returned a response, we use it, otherwise we redirect back to the previous page.
if (isset($redirectResponse) && $redirectResponse instanceof Response) { if ($redirectResponse !== null) {
return $redirectResponse; return $redirectResponse;
} }

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Controller; namespace App\Controller;
use App\Entity\Parameters\AbstractParameter;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use App\Entity\Attachments\Attachment; use App\Entity\Attachments\Attachment;
use App\Entity\Parts\Category; use App\Entity\Parts\Category;
@ -92,7 +93,7 @@ class TypeaheadController extends AbstractController
/** /**
* This function map the parameter type to the class, so we can access its repository * This function map the parameter type to the class, so we can access its repository
* @return class-string * @return class-string<AbstractParameter>
*/ */
private function typeToParameterClass(string $type): string private function typeToParameterClass(string $type): string
{ {
@ -155,7 +156,7 @@ class TypeaheadController extends AbstractController
//Ensure user has the correct permissions //Ensure user has the correct permissions
$this->denyAccessUnlessGranted('read', $test_obj); $this->denyAccessUnlessGranted('read', $test_obj);
/** @var ParameterRepository $repository */ /** @var ParameterRepository<AbstractParameter> $repository */
$repository = $entityManager->getRepository($class); $repository = $entityManager->getRepository($class);
$data = $repository->autocompleteParamName($query); $data = $repository->autocompleteParamName($query);

View file

@ -240,7 +240,10 @@ class UserSettingsController extends AbstractController
$page_need_reload = true; $page_need_reload = true;
} }
/** @var Form $form We need a form implementation for the next calls */ if (!$form instanceof Form) {
throw new RuntimeException('Form is not an instance of Form, so we cannot retrieve the clicked button!');
}
//Remove the avatar attachment from the user if requested //Remove the avatar attachment from the user if requested
if ($form->getClickedButton() && 'remove_avatar' === $form->getClickedButton()->getName() && $user->getMasterPictureAttachment() instanceof Attachment) { if ($form->getClickedButton() && 'remove_avatar' === $form->getClickedButton()->getName() && $user->getMasterPictureAttachment() instanceof Attachment) {
$em->remove($user->getMasterPictureAttachment()); $em->remove($user->getMasterPictureAttachment());

View file

@ -133,6 +133,7 @@ class TagsConstraint extends AbstractConstraint
return; return;
} }
//@phpstan-ignore-next-line Keep this check to ensure that everything has the same structure even if we add a new operator
if ($this->operator === 'NONE') { if ($this->operator === 'NONE') {
$queryBuilder->andWhere($queryBuilder->expr()->not($queryBuilder->expr()->orX(...$tagsExpressions))); $queryBuilder->andWhere($queryBuilder->expr()->not($queryBuilder->expr()->orX(...$tagsExpressions)));
return; return;

View file

@ -87,16 +87,14 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface
if(!$context->getPart() instanceof Part) { if(!$context->getPart() instanceof Part) {
return htmlspecialchars((string) $context->getName()); return htmlspecialchars((string) $context->getName());
} }
if($context->getPart() instanceof Part) {
$tmp = $this->partDataTableHelper->renderName($context->getPart());
if($context->getName() !== null && $context->getName() !== '') {
$tmp .= '<br><b>'.htmlspecialchars($context->getName()).'</b>';
}
return $tmp;
}
//@phpstan-ignore-next-line //Part exists if we reach this point
throw new \RuntimeException('This should never happen!');
$tmp = $this->partDataTableHelper->renderName($context->getPart());
if($context->getName() !== null && $context->getName() !== '') {
$tmp .= '<br><b>'.htmlspecialchars($context->getName()).'</b>';
}
return $tmp;
}, },
]) ])
->add('ipn', TextColumn::class, [ ->add('ipn', TextColumn::class, [

View file

@ -44,15 +44,13 @@ class SQLiteRegexExtensionMiddlewareDriver extends AbstractDriverMiddleware
$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 //Ensure that the function really exists on the connection, as it is marked as experimental according to PHP documentation
if($native_connection instanceof \PDO && method_exists($native_connection, 'sqliteCreateFunction' )) { if($native_connection instanceof \PDO) {
$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);
//Create a new collation for natural sorting //Create a new collation for natural sorting
if (method_exists($native_connection, 'sqliteCreateCollation')) { $native_connection->sqliteCreateCollation('NATURAL_CMP', strnatcmp(...));
$native_connection->sqliteCreateCollation('NATURAL_CMP', strnatcmp(...));
}
} }
} }

View file

@ -531,7 +531,7 @@ abstract class Attachment extends AbstractNamedDBElement
$url = str_replace(' ', '%20', $url); $url = str_replace(' ', '%20', $url);
//Only set if the URL is not empty //Only set if the URL is not empty
if ($url !== null && $url !== '') { if ($url !== '') {
if (str_contains($url, '%BASE%') || str_contains($url, '%MEDIA%')) { if (str_contains($url, '%BASE%') || str_contains($url, '%MEDIA%')) {
throw new InvalidArgumentException('You can not reference internal files via the url field! But nice try!'); throw new InvalidArgumentException('You can not reference internal files via the url field! But nice try!');
} }

View file

@ -33,7 +33,7 @@ use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Serializer\Annotation\Groups;
/** /**
* @template-covariant AT of Attachment * @template AT of Attachment
*/ */
#[ORM\MappedSuperclass(repositoryClass: AttachmentContainingDBElementRepository::class)] #[ORM\MappedSuperclass(repositoryClass: AttachmentContainingDBElementRepository::class)]
abstract class AttachmentContainingDBElement extends AbstractNamedDBElement implements HasMasterAttachmentInterface, HasAttachmentsInterface abstract class AttachmentContainingDBElement extends AbstractNamedDBElement implements HasMasterAttachmentInterface, HasAttachmentsInterface

View file

@ -33,8 +33,8 @@ use Symfony\Component\Validator\Constraints as Assert;
/** /**
* This abstract class is used for companies like suppliers or manufacturers. * This abstract class is used for companies like suppliers or manufacturers.
* *
* @template-covariant AT of Attachment * @template AT of Attachment
* @template-covariant PT of AbstractParameter * @template PT of AbstractParameter
* @extends AbstractPartsContainingDBElement<AT, PT> * @extends AbstractPartsContainingDBElement<AT, PT>
*/ */
#[ORM\MappedSuperclass] #[ORM\MappedSuperclass]

View file

@ -31,8 +31,8 @@ use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Serializer\Annotation\Groups;
/** /**
* @template-covariant AT of Attachment * @template AT of Attachment
* @template-covariant PT of AbstractParameter * @template PT of AbstractParameter
* @extends AbstractStructuralDBElement<AT, PT> * @extends AbstractStructuralDBElement<AT, PT>
*/ */
#[ORM\MappedSuperclass(repositoryClass: AbstractPartsContainingRepository::class)] #[ORM\MappedSuperclass(repositoryClass: AbstractPartsContainingRepository::class)]

View file

@ -53,8 +53,8 @@ use Symfony\Component\Serializer\Annotation\Groups;
* *
* @see \App\Tests\Entity\Base\AbstractStructuralDBElementTest * @see \App\Tests\Entity\Base\AbstractStructuralDBElementTest
* *
* @template-covariant AT of Attachment * @template AT of Attachment
* @template-covariant PT of AbstractParameter * @template PT of AbstractParameter
* @template-use ParametersTrait<PT> * @template-use ParametersTrait<PT>
* @extends AttachmentContainingDBElement<AT> * @extends AttachmentContainingDBElement<AT>
* @uses ParametersTrait<PT> * @uses ParametersTrait<PT>

View file

@ -22,6 +22,8 @@ declare(strict_types=1);
*/ */
namespace App\Entity\LabelSystem; namespace App\Entity\LabelSystem;
use App\Entity\Base\AbstractDBElement;
use App\Entity\Base\AbstractNamedDBElement;
use App\Entity\Parts\Part; use App\Entity\Parts\Part;
use App\Entity\Parts\PartLot; use App\Entity\Parts\PartLot;
use App\Entity\Parts\StorageLocation; use App\Entity\Parts\StorageLocation;
@ -34,7 +36,7 @@ enum LabelSupportedElement: string
/** /**
* Returns the entity class for the given element type * Returns the entity class for the given element type
* @return string * @return class-string<AbstractDBElement>
*/ */
public function getEntityClass(): string public function getEntityClass(): string
{ {

View file

@ -185,7 +185,6 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named
* *
* @return bool|null True, if the part lot is expired. Returns null, if no expiration date was set. * @return bool|null True, if the part lot is expired. Returns null, if no expiration date was set.
* *
* @throws Exception If an error with the DateTime occurs
*/ */
public function isExpired(): ?bool public function isExpired(): ?bool
{ {

View file

@ -103,7 +103,7 @@ class Supplier extends AbstractCompany
protected ?AbstractStructuralDBElement $parent = null; protected ?AbstractStructuralDBElement $parent = null;
/** /**
* @var Collection<int, Orderdetail>|Orderdetail[] * @var Collection<int, Orderdetail>
*/ */
#[ORM\OneToMany(mappedBy: 'supplier', targetEntity: Orderdetail::class)] #[ORM\OneToMany(mappedBy: 'supplier', targetEntity: Orderdetail::class)]
protected Collection $orderdetails; protected Collection $orderdetails;

View file

@ -333,7 +333,6 @@ class Project extends AbstractStructuralDBElement
{ {
//If this project has subprojects, and these have builds part, they must be included in the BOM //If this project has subprojects, and these have builds part, they must be included in the BOM
foreach ($this->getChildren() as $child) { foreach ($this->getChildren() as $child) {
/** @var $child Project */
if (!$child->getBuildPart() instanceof Part) { if (!$child->getBuildPart() instanceof Part) {
continue; continue;
} }

View file

@ -893,8 +893,6 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
* @param string[] $codes An array containing the backup codes * @param string[] $codes An array containing the backup codes
* *
* @return $this * @return $this
*
* @throws Exception If an error with the datetime occurs
*/ */
public function setBackupCodes(array $codes): self public function setBackupCodes(array $codes): self
{ {

View file

@ -43,7 +43,7 @@ class StructuralEntityChoiceHelper
/** /**
* Generates the choice attributes for the given AbstractStructuralDBElement. * Generates the choice attributes for the given AbstractStructuralDBElement.
* @return array|string[] * @return array<string, mixed>
*/ */
public function generateChoiceAttr(AbstractNamedDBElement $choice, Options|array $options): array public function generateChoiceAttr(AbstractNamedDBElement $choice, Options|array $options): array
{ {

View file

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace App\Form\Type\Helper; namespace App\Form\Type\Helper;
use App\Entity\Base\AbstractNamedDBElement; use App\Entity\Base\AbstractNamedDBElement;
use App\Entity\Base\AbstractStructuralDBElement;
use App\Repository\StructuralDBElementRepository; use App\Repository\StructuralDBElementRepository;
use App\Services\Trees\NodesListBuilder; use App\Services\Trees\NodesListBuilder;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
@ -33,6 +34,9 @@ use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\Options;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @template T of AbstractStructuralDBElement
*/
class StructuralEntityChoiceLoader extends AbstractChoiceLoader class StructuralEntityChoiceLoader extends AbstractChoiceLoader
{ {
private ?string $additional_element = null; private ?string $additional_element = null;
@ -90,10 +94,14 @@ class StructuralEntityChoiceLoader extends AbstractChoiceLoader
} }
} }
/** @var class-string<T> $class */
$class = $this->options['class']; $class = $this->options['class'];
/** @var StructuralDBElementRepository $repo */
/** @var StructuralDBElementRepository<T> $repo */
$repo = $this->entityManager->getRepository($class); $repo = $this->entityManager->getRepository($class);
$entities = $repo->getNewEntityFromPath($value, '->'); $entities = $repo->getNewEntityFromPath($value, '->');
$results = []; $results = [];

View file

@ -99,7 +99,6 @@ final class TriStateCheckboxType extends AbstractType implements DataTransformer
* *
* @return mixed The value in the transformed representation * @return mixed The value in the transformed representation
* *
* @throws TransformationFailedException when the transformation fails
*/ */
public function transform(mixed $value) public function transform(mixed $value)
{ {
@ -142,8 +141,6 @@ final class TriStateCheckboxType extends AbstractType implements DataTransformer
* @param mixed $value The value in the transformed representation * @param mixed $value The value in the transformed representation
* *
* @return mixed The value in the original representation * @return mixed The value in the original representation
*
* @throws TransformationFailedException when the transformation fails
*/ */
public function reverseTransform(mixed $value) public function reverseTransform(mixed $value)
{ {

View file

@ -151,7 +151,7 @@ class StructuralDBElementRepository extends AttachmentContainingDBElementReposit
} }
if (null === $entity) { if (null === $entity) {
$class = $this->getClassName(); $class = $this->getClassName();
/** @var AbstractStructuralDBElement $entity */ /** @var TEntityClass $entity */
$entity = new $class; $entity = new $class;
$entity->setName($name); $entity->setName($name);
$entity->setParent($parent); $entity->setParent($parent);
@ -265,7 +265,7 @@ class StructuralDBElementRepository extends AttachmentContainingDBElementReposit
} }
$class = $this->getClassName(); $class = $this->getClassName();
/** @var AbstractStructuralDBElement $entity */ /** @var TEntityClass $entity */
$entity = new $class; $entity = new $class;
$entity->setName($name); $entity->setName($name);

View file

@ -40,12 +40,10 @@ final class UserChecker implements UserCheckerInterface
/** /**
* Checks the user account before authentication. * Checks the user account before authentication.
*
* @throws AccountStatusException
*/ */
public function checkPreAuth(UserInterface $user): void public function checkPreAuth(UserInterface $user): void
{ {
// TODO: Implement checkPreAuth() method. //We don't need to check the user before authentication, just implemented to fulfill the interface
} }
/** /**

View file

@ -69,6 +69,15 @@ class StructuralElementDenormalizer implements DenormalizerInterface, Denormaliz
&& in_array('import', $context['groups'] ?? [], true); && in_array('import', $context['groups'] ?? [], true);
} }
/**
* @template T of AbstractStructuralDBElement
* @param $data
* @phpstan-param class-string<T> $type
* @param string|null $format
* @param array $context
* @return AbstractStructuralDBElement|null
* @phpstan-return T|null
*/
public function denormalize($data, string $type, string $format = null, array $context = []): ?AbstractStructuralDBElement public function denormalize($data, string $type, string $format = null, array $context = []): ?AbstractStructuralDBElement
{ {
//Do not use API Platform's denormalizer //Do not use API Platform's denormalizer
@ -85,7 +94,7 @@ class StructuralElementDenormalizer implements DenormalizerInterface, Denormaliz
$deserialized_entity = $this->denormalizer->denormalize($data, $type, $format, $context); $deserialized_entity = $this->denormalizer->denormalize($data, $type, $format, $context);
//Check if we already have the entity in the database (via path) //Check if we already have the entity in the database (via path)
/** @var StructuralDBElementRepository $repo */ /** @var StructuralDBElementRepository<T> $repo */
$repo = $this->entityManager->getRepository($type); $repo = $this->entityManager->getRepository($type);
$path = $deserialized_entity->getFullPath(AbstractStructuralDBElement::PATH_DELIMITER_ARROW); $path = $deserialized_entity->getFullPath(AbstractStructuralDBElement::PATH_DELIMITER_ARROW);

View file

@ -54,7 +54,7 @@ class StructuralElementFromNameDenormalizer implements DenormalizerInterface
public function denormalize($data, string $type, string $format = null, array $context = []): AbstractStructuralDBElement|null public function denormalize($data, string $type, string $format = null, array $context = []): AbstractStructuralDBElement|null
{ {
//Retrieve the repository for the given type //Retrieve the repository for the given type
/** @var StructuralDBElementRepository $repo */ /** @var StructuralDBElementRepository<T> $repo */
$repo = $this->em->getRepository($type); $repo = $this->em->getRepository($type);
$path_delimiter = $context['path_delimiter'] ?? '->'; $path_delimiter = $context['path_delimiter'] ?? '->';

View file

@ -46,7 +46,7 @@ class MoneyFormatter
public function format(string|float $value, ?Currency $currency = null, int $decimals = 5, bool $show_all_digits = false): string public function format(string|float $value, ?Currency $currency = null, int $decimals = 5, bool $show_all_digits = false): string
{ {
$iso_code = $this->base_currency; $iso_code = $this->base_currency;
if ($currency instanceof Currency && ($currency->getIsoCode() !== null && $currency->getIsoCode() !== '')) { if ($currency instanceof Currency && ($currency->getIsoCode() !== '')) {
$iso_code = $currency->getIsoCode(); $iso_code = $currency->getIsoCode();
} }

View file

@ -153,6 +153,7 @@ class BOMImporter
break; break;
} }
//@phpstan-ignore-next-line We want to keep this check just to be safe when something changes
$new_index = self::MAP_KICAD_PCB_FIELDS[$index] ?? throw new \UnexpectedValueException('Invalid field index!'); $new_index = self::MAP_KICAD_PCB_FIELDS[$index] ?? throw new \UnexpectedValueException('Invalid field index!');
$out[$new_index] = $field; $out[$new_index] = $field;
} }

View file

@ -64,7 +64,7 @@ class EntityImporter
* @phpstan-param class-string<T> $class_name * @phpstan-param class-string<T> $class_name
* @param AbstractStructuralDBElement|null $parent the element which will be used as parent element for new elements * @param AbstractStructuralDBElement|null $parent the element which will be used as parent element for new elements
* @param array $errors an associative array containing all validation errors * @param array $errors an associative array containing all validation errors
* @param-out array<string, array{'entity': object, 'violations': ConstraintViolationListInterface}> $errors * @param-out list<array{'entity': object, 'violations': ConstraintViolationListInterface}> $errors
* *
* @return AbstractNamedDBElement[] An array containing all valid imported entities (with the type $class_name) * @return AbstractNamedDBElement[] An array containing all valid imported entities (with the type $class_name)
* @return T[] * @return T[]
@ -133,13 +133,15 @@ class EntityImporter
if ($repo instanceof StructuralDBElementRepository) { if ($repo instanceof StructuralDBElementRepository) {
$entities = $repo->getNewEntityFromPath($new_path); $entities = $repo->getNewEntityFromPath($new_path);
$entity = end($entities); $entity = end($entities);
if ($entity === false) {
throw new InvalidArgumentException('getNewEntityFromPath returned an empty array!');
}
} else { //Otherwise just create a new entity } else { //Otherwise just create a new entity
$entity = new $class_name; $entity = new $class_name;
$entity->setName($name); $entity->setName($name);
} }
//Validate entity //Validate entity
$tmp = $this->validator->validate($entity); $tmp = $this->validator->validate($entity);
//If no error occured, write entry to DB: //If no error occured, write entry to DB:
@ -227,6 +229,11 @@ class EntityImporter
//Iterate over each $entity write it to DB. //Iterate over each $entity write it to DB.
foreach ($entities as $key => $entity) { foreach ($entities as $key => $entity) {
//Ensure that entity is a NamedDBElement
if (!$entity instanceof AbstractNamedDBElement) {
throw new \RuntimeException("Encountered an entity that is not a NamedDBElement!");
}
//Validate entity //Validate entity
$tmp = $this->validator->validate($entity); $tmp = $this->validator->validate($entity);
@ -281,7 +288,7 @@ class EntityImporter
* *
* @param File $file the file that should be used for importing * @param File $file the file that should be used for importing
* @param array $options options for the import process * @param array $options options for the import process
* @param AbstractNamedDBElement[] $entities The imported entities are returned in this array * @param-out AbstractNamedDBElement[] $entities The imported entities are returned in this array
* *
* @return array<string, array{'entity': object, 'violations': ConstraintViolationListInterface}> An associative array containing an ConstraintViolationList and the entity name as key are returned, * @return array<string, array{'entity': object, 'violations': ConstraintViolationListInterface}> An associative array containing an ConstraintViolationList and the entity name as key are returned,
* if an error happened during validation. When everything was successfully, the array should be empty. * if an error happened during validation. When everything was successfully, the array should be empty.
@ -317,7 +324,7 @@ class EntityImporter
* @param array $options options for the import process * @param array $options options for the import process
* @param-out array<string, array{'entity': object, 'violations': ConstraintViolationListInterface}> $errors * @param-out array<string, array{'entity': object, 'violations': ConstraintViolationListInterface}> $errors
* *
* @return array an array containing the deserialized elements * @return AbstractNamedDBElement[] an array containing the deserialized elements
*/ */
public function importFile(File $file, array $options = [], array &$errors = []): array public function importFile(File $file, array $options = [], array &$errors = []): array
{ {

View file

@ -205,10 +205,6 @@ trait PKImportHelperTrait
*/ */
protected function setIDOfEntity(AbstractDBElement $element, int|string $id): void protected function setIDOfEntity(AbstractDBElement $element, int|string $id): void
{ {
if (!is_int($id) && !is_string($id)) {
throw new \InvalidArgumentException('ID must be an integer or string');
}
$id = (int) $id; $id = (int) $id;
$metadata = $this->em->getClassMetadata($element::class); $metadata = $this->em->getClassMetadata($element::class);

View file

@ -771,11 +771,6 @@ class OEMSecretsProvider implements InfoProviderInterface
// Logic to extract parameters from the description // Logic to extract parameters from the description
$extractedParameters = $this->parseDescriptionToParameters($description) ?? []; $extractedParameters = $this->parseDescriptionToParameters($description) ?? [];
// Ensure that $extractedParameters is an array
if (!is_array($extractedParameters)) {
$extractedParameters = [];
}
foreach ($extractedParameters as $newParam) { foreach ($extractedParameters as $newParam) {
$isDuplicate = false; $isDuplicate = false;
foreach ($parameters as $existingParam) { foreach ($parameters as $existingParam) {

View file

@ -146,11 +146,11 @@ final class LabelExampleElementsGenerator
throw new InvalidArgumentException('$class must be an child of AbstractStructuralDBElement'); throw new InvalidArgumentException('$class must be an child of AbstractStructuralDBElement');
} }
/** @var AbstractStructuralDBElement $parent */ /** @var T $parent */
$parent = new $class(); $parent = new $class();
$parent->setName('Example'); $parent->setName('Example');
/** @var AbstractStructuralDBElement $child */ /** @var T $child */
$child = new $class(); $child = new $class();
$child->setName((new ReflectionClass($class))->getShortName()); $child->setName((new ReflectionClass($class))->getShortName());
$child->setParent($parent); $child->setParent($parent);

View file

@ -62,10 +62,6 @@ final class LabelGenerator
*/ */
public function generateLabel(LabelOptions $options, object|array $elements): string public function generateLabel(LabelOptions $options, object|array $elements): string
{ {
if (!is_array($elements) && !is_object($elements)) {
throw new InvalidArgumentException('$element must be an object or an array of objects!');
}
if (!is_array($elements)) { if (!is_array($elements)) {
$elements = [$elements]; $elements = [$elements];
} }

View file

@ -74,8 +74,7 @@ final class LabelProfileDropdownHelper
$secure_class_name = $this->tagGenerator->getElementTypeCacheTag(LabelProfile::class); $secure_class_name = $this->tagGenerator->getElementTypeCacheTag(LabelProfile::class);
$key = 'profile_dropdown_'.$this->keyGenerator->generateKey().'_'.$secure_class_name.'_'.$type->value; $key = 'profile_dropdown_'.$this->keyGenerator->generateKey().'_'.$secure_class_name.'_'.$type->value;
/** @var LabelProfileRepository $repo */
$repo = $this->entityManager->getRepository(LabelProfile::class); $repo = $this->entityManager->getRepository(LabelProfile::class);
return $this->cache->get($key, function (ItemInterface $item) use ($repo, $type, $secure_class_name) { return $this->cache->get($key, function (ItemInterface $item) use ($repo, $type, $secure_class_name) {

View file

@ -53,7 +53,6 @@ final class PartsTableActionHandler
{ {
$id_array = explode(',', $ids); $id_array = explode(',', $ids);
/** @var PartRepository $repo */
$repo = $this->entityManager->getRepository(Part::class); $repo = $this->entityManager->getRepository(Part::class);
return $repo->getElementsFromIDArray($id_array); return $repo->getElementsFromIDArray($id_array);

View file

@ -40,9 +40,6 @@ class BannerHelper
public function getBanner(): string public function getBanner(): string
{ {
$banner = $this->partdb_banner; $banner = $this->partdb_banner;
if (!is_string($banner)) {
throw new \RuntimeException('The parameter "partdb.banner" must be a string.');
}
if ($banner === '') { if ($banner === '') {
$banner_path = $this->project_dir $banner_path = $this->project_dir
.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'banner.md'; .DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'banner.md';

View file

@ -122,7 +122,6 @@ class StatisticsHelper
throw new InvalidArgumentException('No count for the given type available!'); throw new InvalidArgumentException('No count for the given type available!');
} }
/** @var EntityRepository $repo */
$repo = $this->em->getRepository($arr[$type]); $repo = $this->em->getRepository($arr[$type]);
return $repo->count([]); return $repo->count([]);

View file

@ -23,9 +23,11 @@ declare(strict_types=1);
namespace App\Services\Trees; namespace App\Services\Trees;
use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractDBElement;
use App\Entity\Base\AbstractNamedDBElement;
use App\Entity\Base\AbstractStructuralDBElement; use App\Entity\Base\AbstractStructuralDBElement;
use App\Repository\AttachmentContainingDBElementRepository; use App\Repository\AttachmentContainingDBElementRepository;
use App\Repository\DBElementRepository; use App\Repository\DBElementRepository;
use App\Repository\NamedDBElementRepository;
use App\Repository\StructuralDBElementRepository; use App\Repository\StructuralDBElementRepository;
use App\Services\Cache\ElementCacheTagGenerator; use App\Services\Cache\ElementCacheTagGenerator;
use App\Services\Cache\UserCacheKeyGenerator; use App\Services\Cache\UserCacheKeyGenerator;
@ -51,7 +53,7 @@ class NodesListBuilder
* Gets a flattened hierarchical tree. Useful for generating option lists. * Gets a flattened hierarchical tree. Useful for generating option lists.
* In difference to the Repository Function, the results here are cached. * In difference to the Repository Function, the results here are cached.
* *
* @template T of AbstractDBElement * @template T of AbstractNamedDBElement
* *
* @param string $class_name the class name of the entity you want to retrieve * @param string $class_name the class name of the entity you want to retrieve
* @phpstan-param class-string<T> $class_name * @phpstan-param class-string<T> $class_name
@ -69,7 +71,7 @@ class NodesListBuilder
$ids = $this->getFlattenedIDs($class_name, $parent); $ids = $this->getFlattenedIDs($class_name, $parent);
//Retrieve the elements from the IDs, the order is the same as in the $ids array //Retrieve the elements from the IDs, the order is the same as in the $ids array
/** @var DBElementRepository $repo */ /** @var NamedDBElementRepository<T> $repo */
$repo = $this->em->getRepository($class_name); $repo = $this->em->getRepository($class_name);
if ($repo instanceof AttachmentContainingDBElementRepository) { if ($repo instanceof AttachmentContainingDBElementRepository) {
@ -81,7 +83,9 @@ class NodesListBuilder
/** /**
* This functions returns the (cached) list of the IDs of the elements for the flattened tree. * This functions returns the (cached) list of the IDs of the elements for the flattened tree.
* @template T of AbstractNamedDBElement
* @param string $class_name * @param string $class_name
* @phpstan-param class-string<T> $class_name
* @param AbstractStructuralDBElement|null $parent * @param AbstractStructuralDBElement|null $parent
* @return int[] * @return int[]
*/ */
@ -96,10 +100,11 @@ class NodesListBuilder
// Invalidate when groups, an element with the class or the user changes // Invalidate when groups, an element with the class or the user changes
$item->tag(['groups', 'tree_list', $this->keyGenerator->generateKey(), $secure_class_name]); $item->tag(['groups', 'tree_list', $this->keyGenerator->generateKey(), $secure_class_name]);
/** @var StructuralDBElementRepository $repo */ /** @var NamedDBElementRepository<T> $repo */
$repo = $this->em->getRepository($class_name); $repo = $this->em->getRepository($class_name);
return array_map(static fn(AbstractDBElement $element) => $element->getID(), $repo->getFlatList($parent)); return array_map(static fn(AbstractDBElement $element) => $element->getID(),
$repo instanceof AbstractStructuralDBElement ? $repo->getFlatList($parent) : $repo->getFlatList());
}); });
} }

View file

@ -33,6 +33,7 @@ use App\Entity\Parts\Supplier;
use App\Entity\ProjectSystem\Project; use App\Entity\ProjectSystem\Project;
use App\Helpers\Trees\TreeViewNode; use App\Helpers\Trees\TreeViewNode;
use App\Helpers\Trees\TreeViewNodeIterator; use App\Helpers\Trees\TreeViewNodeIterator;
use App\Repository\NamedDBElementRepository;
use App\Repository\StructuralDBElementRepository; use App\Repository\StructuralDBElementRepository;
use App\Services\Cache\ElementCacheTagGenerator; use App\Services\Cache\ElementCacheTagGenerator;
use App\Services\Cache\UserCacheKeyGenerator; use App\Services\Cache\UserCacheKeyGenerator;
@ -219,6 +220,7 @@ class TreeViewGenerator
* The treeview is generic, that means the href are null and ID values are set. * The treeview is generic, that means the href are null and ID values are set.
* *
* @param string $class The class for which the tree should be generated * @param string $class The class for which the tree should be generated
* @phpstan-param class-string<AbstractNamedDBElement> $class
* @param AbstractStructuralDBElement|null $parent the parent the root elements should have * @param AbstractStructuralDBElement|null $parent the parent the root elements should have
* *
* @return TreeViewNode[] * @return TreeViewNode[]
@ -232,12 +234,12 @@ class TreeViewGenerator
throw new InvalidArgumentException('$parent must be of the type $class!'); throw new InvalidArgumentException('$parent must be of the type $class!');
} }
/** @var StructuralDBElementRepository $repo */ /** @var NamedDBElementRepository<AbstractNamedDBElement> $repo */
$repo = $this->em->getRepository($class); $repo = $this->em->getRepository($class);
//If we just want a part of a tree, don't cache it //If we just want a part of a tree, don't cache it
if ($parent instanceof AbstractStructuralDBElement) { if ($parent instanceof AbstractStructuralDBElement) {
return $repo->getGenericNodeTree($parent); return $repo->getGenericNodeTree($parent); //@phpstan-ignore-line PHPstan does not seem to recognize, that we have a StructuralDBElementRepository here, which have 1 argument
} }
$secure_class_name = $this->tagGenerator->getElementTypeCacheTag($class); $secure_class_name = $this->tagGenerator->getElementTypeCacheTag($class);
@ -246,7 +248,7 @@ class TreeViewGenerator
return $this->cache->get($key, function (ItemInterface $item) use ($repo, $parent, $secure_class_name) { return $this->cache->get($key, function (ItemInterface $item) use ($repo, $parent, $secure_class_name) {
// Invalidate when groups, an element with the class or the user changes // Invalidate when groups, an element with the class or the user changes
$item->tag(['groups', 'tree_treeview', $this->keyGenerator->generateKey(), $secure_class_name]); $item->tag(['groups', 'tree_treeview', $this->keyGenerator->generateKey(), $secure_class_name]);
return $repo->getGenericNodeTree($parent); return $repo->getGenericNodeTree($parent); //@phpstan-ignore-line
}); });
} }
} }