diff --git a/README.md b/README.md
index ac42d2d..4462263 100644
--- a/README.md
+++ b/README.md
@@ -36,6 +36,12 @@ composer require digitalrevolution/ipp
$ipp->print($printer, $ippFile);
```
+### Print job validation
+To test a print operation without actually printing anything, you can use the `validatePrintJob` operation instead.
+```php
+ $ipp->validatePrintJob($printer, $ippFile);
+```
+
### Fetch job attributes
```php
diff --git a/composer.json b/composer.json
index ad3b172..31bb970 100644
--- a/composer.json
+++ b/composer.json
@@ -16,23 +16,23 @@
},
"require": {
"php": "^8.3",
+ "digitalrevolution/utils": "^1.0",
"nyholm/psr7": "^1.8",
"psr/http-client": "^1.0",
- "psr/log": "^3.0",
- "digitalrevolution/utils": "^1.0"
+ "psr/log": "^3.0"
},
"require-dev": {
"digitalrevolution/accessorpair-constraint": "^v2.4.1",
+ "digitalrevolution/php-codesniffer-baseline": "^1.3",
"digitalrevolution/phpunit-file-coverage-inspection": "^3.0",
- "digitalrevolution/php-codesniffer-baseline": "^1.1",
"phpmd/phpmd": "^2.12",
"phpstan/extension-installer": "^1.2",
"phpstan/phpstan": "^2.0.0",
"phpstan/phpstan-phpunit": "^2.0.0",
"phpunit/phpunit": "^12.0",
"roave/security-advisories": "dev-latest",
- "squizlabs/php_codesniffer": "^4.0",
- "slevomat/coding-standard": "^8.16"
+ "slevomat/coding-standard": "^8.16",
+ "squizlabs/php_codesniffer": "^4.0"
},
"scripts": {
"baseline": [
diff --git a/phpcs.baseline.xml b/phpcs.baseline.xml
new file mode 100644
index 0000000..f18701d
--- /dev/null
+++ b/phpcs.baseline.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/phpmd.baseline.xml b/phpmd.baseline.xml
new file mode 100644
index 0000000..8ca7aec
--- /dev/null
+++ b/phpmd.baseline.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/src/Client/CupsIppHttpClient.php b/src/Client/CupsIppHttpClient.php
index 50186a0..599b7d8 100644
--- a/src/Client/CupsIppHttpClient.php
+++ b/src/Client/CupsIppHttpClient.php
@@ -14,8 +14,7 @@
class CupsIppHttpClient implements IppHttpClientInterface
{
- private const int HTTP_STATUS_UNAUTHORIZED = 401;
- private const int HTTP_STATUS_SERVER_ERROR = 500;
+ private const int HTTP_STATUS_BAD_REQUEST = 400;
public function __construct(private readonly IppServer $server, private readonly ClientInterface $client)
{
@@ -42,7 +41,7 @@ public function sendRequest(IppOperation $operation): ResponseInterface
$response = $this->client->sendRequest($request);
// a server error won't be an ipp response we can parse.
- if ($response->getStatusCode() === self::HTTP_STATUS_UNAUTHORIZED || $response->getStatusCode() >= self::HTTP_STATUS_SERVER_ERROR) {
+ if ($response->getStatusCode() >= self::HTTP_STATUS_BAD_REQUEST) {
throw new IppRequestException($request, $response);
}
diff --git a/src/Entity/IppJob.php b/src/Entity/IppJob.php
index 07c71fd..83b490c 100644
--- a/src/Entity/IppJob.php
+++ b/src/Entity/IppJob.php
@@ -12,15 +12,21 @@ class IppJob
{
private int $id;
private string $uri;
+ private ?string $name = null;
private JobStateEnum $jobState;
private JobStateReasonEnum $jobStateReason;
- private ?string $userName;
- private ?int $fileSize;
- private ?int $numberOfDocuments;
- private ?int $copies;
- private ?DateTimeInterface $creationDate;
- private ?DateTimeInterface $completionDate;
- private ?string $documentFormat;
+ private ?string $userName = null;
+ private ?int $fileSize = null;
+ private ?int $numberOfDocuments = null;
+ private ?int $copies = null;
+ private ?DateTimeInterface $creationDate = null;
+ private ?DateTimeInterface $processingDate = null;
+ private ?DateTimeInterface $completionDate = null;
+ private ?string $documentFormat = null;
+ private ?string $documentName = null;
+ private ?string $printerUri = null;
+ private ?DateTimeInterface $printerUpSince = null;
+ private ?int $priority = null;
public function getId(): int
{
@@ -46,6 +52,18 @@ public function setUri(string $uri): self
return $this;
}
+ public function getName(): ?string
+ {
+ return $this->name;
+ }
+
+ public function setName(?string $name): self
+ {
+ $this->name = $name;
+
+ return $this;
+ }
+
public function getJobStateReason(): JobStateReasonEnum
{
return $this->jobStateReason;
@@ -130,6 +148,18 @@ public function setCreationDate(?DateTimeInterface $creationDate): self
return $this;
}
+ public function getProcessingDate(): ?DateTimeInterface
+ {
+ return $this->processingDate;
+ }
+
+ public function setProcessingDate(?DateTimeInterface $processingDate): self
+ {
+ $this->processingDate = $processingDate;
+
+ return $this;
+ }
+
public function getCompletionDate(): ?DateTimeInterface
{
return $this->completionDate;
@@ -153,4 +183,52 @@ public function setDocumentFormat(?string $documentFormat): self
return $this;
}
+
+ public function getDocumentName(): ?string
+ {
+ return $this->documentName;
+ }
+
+ public function setDocumentName(?string $documentName): self
+ {
+ $this->documentName = $documentName;
+
+ return $this;
+ }
+
+ public function getPrinterUpSince(): ?DateTimeInterface
+ {
+ return $this->printerUpSince;
+ }
+
+ public function setPrinterUpSince(?DateTimeInterface $printerUpTime): self
+ {
+ $this->printerUpSince = $printerUpTime;
+
+ return $this;
+ }
+
+ public function getPrinterUri(): ?string
+ {
+ return $this->printerUri;
+ }
+
+ public function setPrinterUri(?string $setPrinterUri): self
+ {
+ $this->printerUri = $setPrinterUri;
+
+ return $this;
+ }
+
+ public function getPriority(): ?int
+ {
+ return $this->priority;
+ }
+
+ public function setPriority(?int $priority): self
+ {
+ $this->priority = $priority;
+
+ return $this;
+ }
}
diff --git a/src/Entity/IppPrinter.php b/src/Entity/IppPrinter.php
index 9f6da52..1a5efc4 100644
--- a/src/Entity/IppPrinter.php
+++ b/src/Entity/IppPrinter.php
@@ -4,19 +4,41 @@
namespace DR\Ipp\Entity;
+use DateTimeInterface;
+use DR\Ipp\Enum\AuthenticationSupportEnum;
use DR\Ipp\Enum\PrinterStateEnum;
-use DR\Ipp\Enum\PrinterStateReasonEnum;
+use DR\Ipp\Enum\PrintQualityEnum;
+use DR\Ipp\Enum\SecuritySupportEnum;
class IppPrinter
{
private string $hostname;
private string $deviceUri;
private string $location;
+ private ?string $printerUri;
private ?string $trayName = null;
private ?string $ppdName = null;
private ?PrinterStateEnum $printerState = null;
- private ?PrinterStateReasonEnum $printerStateReason = null;
+ private ?string $printerStateReason = null;
private ?string $printerType = null;
+ /** @var AuthenticationSupportEnum[] */
+ private array $uriAuthSupported = [];
+ /** @var SecuritySupportEnum[] */
+ private array $uriSecuritySupported = [];
+ private ?string $defaultDocumentFormat = null;
+ /** @var string[] */
+ private array $documentFormatsSupported = [];
+ private ?bool $acceptingJobs = null;
+ private ?int $queuedJobsCount = null;
+ private ?DateTimeInterface $upSince = null;
+ private ?int $fileSizeLimit = null;
+ private ?int $pageLimit = null;
+ private ?string $defaultMedia = null;
+ /** @var string[] */
+ private array $supportedMedia = [];
+ private ?PrintQualityEnum $defaultQuality = null;
+ /** @var PrintQualityEnum[] */
+ private array $printQualitiesSupported = [];
public function getHostname(): string
{
@@ -54,6 +76,18 @@ public function setLocation(string $location): self
return $this;
}
+ public function getPrinterUri(): ?string
+ {
+ return $this->printerUri;
+ }
+
+ public function setPrinterUri(?string $printerUri): self
+ {
+ $this->printerUri = $printerUri;
+
+ return $this;
+ }
+
public function getTrayName(): ?string
{
return $this->trayName;
@@ -90,12 +124,12 @@ public function setPrinterState(?PrinterStateEnum $printerState): self
return $this;
}
- public function getPrinterStateReason(): ?PrinterStateReasonEnum
+ public function getPrinterStateReason(): ?string
{
return $this->printerStateReason;
}
- public function setPrinterStateReason(?PrinterStateReasonEnum $printerStateReason): self
+ public function setPrinterStateReason(?string $printerStateReason): self
{
$this->printerStateReason = $printerStateReason;
@@ -113,4 +147,190 @@ public function setPrinterType(?string $printerType): self
return $this;
}
+
+ public function getUpSince(): ?DateTimeInterface
+ {
+ return $this->upSince;
+ }
+
+ public function setUpSince(?DateTimeInterface $upSince): self
+ {
+ $this->upSince = $upSince;
+
+ return $this;
+ }
+
+ public function getQueuedJobsCount(): ?int
+ {
+ return $this->queuedJobsCount;
+ }
+
+ public function setQueuedJobsCount(?int $queuedJobsCount): self
+ {
+ $this->queuedJobsCount = $queuedJobsCount;
+
+ return $this;
+ }
+
+ public function getAcceptingJobs(): ?bool
+ {
+ return $this->acceptingJobs;
+ }
+
+ public function setAcceptingJobs(?bool $acceptingJobs): self
+ {
+ $this->acceptingJobs = $acceptingJobs;
+
+ return $this;
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getDocumentFormatsSupported(): array
+ {
+ return $this->documentFormatsSupported;
+ }
+
+ /**
+ * @param string[] $documentFormatsSupported
+ */
+ public function setDocumentFormatsSupported(array $documentFormatsSupported): self
+ {
+ $this->documentFormatsSupported = $documentFormatsSupported;
+
+ return $this;
+ }
+
+ public function getDefaultDocumentFormat(): ?string
+ {
+ return $this->defaultDocumentFormat;
+ }
+
+ public function setDefaultDocumentFormat(?string $defaultDocumentFormat): self
+ {
+ $this->defaultDocumentFormat = $defaultDocumentFormat;
+
+ return $this;
+ }
+
+ /**
+ * @return AuthenticationSupportEnum[]|null
+ */
+ public function getUriAuthSupported(): ?array
+ {
+ return $this->uriAuthSupported;
+ }
+
+ /**
+ * @param AuthenticationSupportEnum[] $uriAuthSupported
+ */
+ public function setUriAuthSupported(array $uriAuthSupported): self
+ {
+ $this->uriAuthSupported = $uriAuthSupported;
+
+ return $this;
+ }
+
+ /**
+ * @return SecuritySupportEnum[]
+ */
+ public function getUriSecuritySupported(): array
+ {
+ return $this->uriSecuritySupported;
+ }
+
+ /**
+ * @param SecuritySupportEnum[] $uriSecuritySupported
+ */
+ public function setUriSecuritySupported(array $uriSecuritySupported): self
+ {
+ $this->uriSecuritySupported = $uriSecuritySupported;
+
+ return $this;
+ }
+
+ public function getFileSizeLimit(): ?int
+ {
+ return $this->fileSizeLimit;
+ }
+
+ public function setFileSizeLimit(?int $fileSizeLimit): self
+ {
+ $this->fileSizeLimit = $fileSizeLimit;
+
+ return $this;
+ }
+
+ public function getPageLimit(): ?int
+ {
+ return $this->pageLimit;
+ }
+
+ public function setPageLimit(?int $pageLimit): self
+ {
+ $this->pageLimit = $pageLimit;
+
+ return $this;
+ }
+
+ public function getDefaultMedia(): ?string
+ {
+ return $this->defaultMedia;
+ }
+
+ public function setDefaultMedia(?string $defaultMedia): self
+ {
+ $this->defaultMedia = $defaultMedia;
+
+ return $this;
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getSupportedMedia(): array
+ {
+ return $this->supportedMedia;
+ }
+
+ /**
+ * @param string[] $supportedMedia
+ */
+ public function setSupportedMedia(array $supportedMedia): self
+ {
+ $this->supportedMedia = $supportedMedia;
+
+ return $this;
+ }
+
+ public function getDefaultQuality(): ?PrintQualityEnum
+ {
+ return $this->defaultQuality;
+ }
+
+ public function setDefaultQuality(?PrintQualityEnum $defaultQuality): self
+ {
+ $this->defaultQuality = $defaultQuality;
+
+ return $this;
+ }
+
+ /**
+ * @return PrintQualityEnum[]
+ */
+ public function getPrintQualitiesSupported(): array
+ {
+ return $this->printQualitiesSupported;
+ }
+
+ /**
+ * @param PrintQualityEnum[] $printQualitiesSupported
+ */
+ public function setPrintQualitiesSupported(array $printQualitiesSupported): self
+ {
+ $this->printQualitiesSupported = $printQualitiesSupported;
+
+ return $this;
+ }
}
diff --git a/src/Enum/AuthenticationSupportEnum.php b/src/Enum/AuthenticationSupportEnum.php
new file mode 100644
index 0000000..6a7bea7
--- /dev/null
+++ b/src/Enum/AuthenticationSupportEnum.php
@@ -0,0 +1,17 @@
+getAttribute($attributes, 'job-uri')?->getValue();
+ $jobName = $this->getAttribute($attributes, 'job-name')?->getValue();
$jobState = $this->getAttribute($attributes, 'job-state')?->getValue();
$stateReason = $this->getAttribute($attributes, 'job-state-reasons')?->getValue();
$nrOfDocuments = $this->getAttribute($attributes, 'number-of-documents')?->getValue();
@@ -31,12 +33,18 @@ public function create(array $attributes): ?IppJob
$fileSize = $this->getAttribute($attributes, 'job-k-octets')?->getValue();
$copies = $this->getAttribute($attributes, 'copies')?->getValue();
$creationDate = $this->getAttribute($attributes, 'date-time-at-creation')?->getValue();
+ $processingDate = $this->getAttribute($attributes, 'date-time-at-processing')?->getValue();
$completionDate = $this->getAttribute($attributes, 'date-time-at-completed')?->getValue();
$documentFormat = $this->getAttribute($attributes, 'document-format')?->getValue();
+ $documentName = $this->getAttribute($attributes, 'document-name')?->getValue();
+ $printerUri = $this->getAttribute($attributes, 'job-printer-uri')?->getValue();
+ $printerUptime = $this->getAttribute($attributes, 'job-printer-up-time')?->getValue();
+ $priority = $this->getAttribute($attributes, 'job-priority')?->getValue();
$job = new IppJob();
$job->setId(Assert::integer($jobId));
$job->setUri(Assert::string($jobUri));
+ $job->setName($jobName === null ? null : Assert::string($jobName));
$job->setJobState(JobStateEnum::from(Assert::integer($jobState)));
$job->setJobStateReason(JobStateReasonEnum::tryFrom(Assert::string($stateReason)) ?? JobStateReasonEnum::Unknown);
$job->setNumberOfDocuments($nrOfDocuments === null ? null : Assert::integer($nrOfDocuments));
@@ -44,8 +52,13 @@ public function create(array $attributes): ?IppJob
$job->setFileSize($fileSize === null ? null : Assert::integer($fileSize));
$job->setCopies($copies === null ? null : Assert::integer($copies));
$job->setCreationDate($creationDate instanceof DateTimeInterface ? $creationDate : null);
+ $job->setProcessingDate($processingDate instanceof DateTimeInterface ? $processingDate : null);
$job->setCompletionDate($completionDate instanceof DateTimeInterface ? $completionDate : null);
$job->setDocumentFormat($documentFormat === null ? null : Assert::string($documentFormat));
+ $job->setDocumentName($documentName === null ? null : Assert::string($documentName));
+ $job->setPrinterUri($printerUri === null ? null : Assert::string($printerUri));
+ $job->setPrinterUpSince($printerUptime === null ? null : (new DateTimeImmutable())->setTimeStamp(Assert::nonNegativeInt($printerUptime)));
+ $job->setPriority($priority === null ? null : Assert::integer($priority));
return $job;
}
diff --git a/src/Factory/IppPrinterFactory.php b/src/Factory/IppPrinterFactory.php
index 03e063e..d86be9c 100644
--- a/src/Factory/IppPrinterFactory.php
+++ b/src/Factory/IppPrinterFactory.php
@@ -4,9 +4,13 @@
namespace DR\Ipp\Factory;
+use BackedEnum;
+use DateTimeImmutable;
use DR\Ipp\Entity\IppPrinter;
+use DR\Ipp\Enum\AuthenticationSupportEnum;
use DR\Ipp\Enum\PrinterStateEnum;
-use DR\Ipp\Enum\PrinterStateReasonEnum;
+use DR\Ipp\Enum\PrintQualityEnum;
+use DR\Ipp\Enum\SecuritySupportEnum;
use DR\Ipp\Protocol\IppAttribute;
use DR\Utils\Assert;
@@ -22,15 +26,46 @@ public function create(array $attributes): ?IppPrinter
return null;
}
+ $stateReason = Assert::string($this->getAttribute($attributes, 'printer-state-reasons')?->getValue());
+ $printerType = $this->getAttribute($attributes, 'printer-make-and-model')?->getValue();
+ $queuedJobsCount = $this->getAttribute($attributes, 'queued-job-count')?->getValue();
+ $uriAuthSupported = $this->getEnumValues($attributes, 'uri-authentication-supported', AuthenticationSupportEnum::class);
+ $uriSecuritySupported = $this->getEnumValues($attributes, 'uri-security-supported', SecuritySupportEnum::class);
+ $documentFormatDefault = $this->getAttribute($attributes, 'document-format-default')?->getValue();
+ /** @phpstan-var string[]|null $documentFormatsSupported */
+ $documentFormatsSupported = $this->getAttribute($attributes, 'document-format-supported')?->getValue();
+ $acceptingJobs = $this->getAttribute($attributes, 'printer-is-accepting-jobs')?->getValue();
+ $printerUri = $this->getAttribute($attributes, 'printer-uri-supported')?->getValue();
+ $upTime = $this->getAttribute($attributes, 'printer-up-time')?->getValue();
+ $fileSizeLimit = $this->getAttribute($attributes, 'job-k-limit')?->getValue();
+ $pageLimit = $this->getAttribute($attributes, 'job-page-limit')?->getValue();
+ /** @phpstan-var string[]|null $mediaSupported */
+ $mediaSupported = $this->getAttribute($attributes, 'media-supported')?->getValue();
+ $defaultMedia = $this->getAttribute($attributes, 'media-default')?->getValue();
+ $defaultPrintQuality = $this->getAttribute($attributes, 'print-quality-default')?->getValue();
+ $printQualitiesSupported = $this->getEnumValues($attributes, 'print-quality-supported', PrintQualityEnum::class);
+
$printer = new IppPrinter();
$printer->setHostname(Assert::string($name->getValue()));
$printer->setLocation(Assert::string($this->getAttribute($attributes, 'printer-location')?->getValue()));
$printer->setDeviceUri(Assert::string($this->getAttribute($attributes, 'device-uri')?->getValue()));
$printer->setPrinterState(PrinterStateEnum::from(Assert::integer($this->getAttribute($attributes, 'printer-state')?->getValue())));
- $stateReason = PrinterStateReasonEnum::tryFrom(Assert::string($this->getAttribute($attributes, 'printer-state-reasons')?->getValue()));
- $printer->setPrinterStateReason($stateReason ?? PrinterStateReasonEnum::Unknown);
- $printerType = $this->getAttribute($attributes, 'printer-make-and-model')?->getValue();
+ $printer->setPrinterStateReason($stateReason);
$printer->setPrinterType($printerType === null ? null : Assert::string($printerType));
+ $printer->setUriAuthSupported($uriAuthSupported);
+ $printer->setUriSecuritySupported($uriSecuritySupported);
+ $printer->setDefaultDocumentFormat($documentFormatDefault === null ? null : Assert::string($documentFormatDefault));
+ $printer->setDocumentFormatsSupported($documentFormatsSupported === null ? [] : Assert::isArray($documentFormatsSupported));
+ $printer->setAcceptingJobs($acceptingJobs === null ? null : Assert::boolean($acceptingJobs));
+ $printer->setQueuedJobsCount($queuedJobsCount === null ? null : Assert::integer($queuedJobsCount));
+ $printer->setPrinterUri($printerUri === null ? null : Assert::string($printerUri));
+ $printer->setUpSince($upTime === null ? null : (new DateTimeImmutable())->setTimeStamp(Assert::integer($upTime)));
+ $printer->setFileSizeLimit($fileSizeLimit > 0 ? Assert::integer($fileSizeLimit) : null);
+ $printer->setPageLimit($pageLimit > 0 ? Assert::integer($pageLimit) : null);
+ $printer->setSupportedMedia($mediaSupported === null ? [] : Assert::isArray($mediaSupported));
+ $printer->setDefaultMedia($defaultMedia === null ? null : Assert::string($defaultMedia));
+ $printer->setDefaultQuality($defaultPrintQuality === null ? null : PrintQualityEnum::tryFrom(Assert::nonNegativeInt($defaultPrintQuality)));
+ $printer->setPrintQualitiesSupported($printQualitiesSupported);
return $printer;
}
@@ -42,4 +77,33 @@ private function getAttribute(array $attributes, string $name): ?IppAttribute
{
return $attributes[$name] ?? null;
}
+
+ /**
+ * @param IppAttribute[] $attributes
+ * @template T of BackedEnum
+ * @param class-string $enumClass
+ *
+ * @return T[]
+ */
+ private function getEnumValues(array $attributes, string $name, string $enumClass): array
+ {
+ /** @phpstan-var int|string|null $value */
+ $value = $this->getAttribute($attributes, $name)?->getValue();
+ if ($value === null) {
+ return [];
+ }
+
+ if (is_array($value) === false) {
+ return $enumClass::tryFrom($value) === null ? [] : [$enumClass::from($value)];
+ }
+
+ $result = [];
+ foreach ($value as $item) {
+ if ($enumClass::tryFrom($item) !== null) {
+ $result[] = $enumClass::from($item);
+ }
+ }
+
+ return $result;
+ }
}
diff --git a/src/Ipp.php b/src/Ipp.php
index 104b212..0105dff 100644
--- a/src/Ipp.php
+++ b/src/Ipp.php
@@ -115,4 +115,14 @@ public function cancelJob(IppJob $job): IppResponseInterface
return $this->cancelJob->cancel($job);
}
+
+ /**
+ * @throws ClientExceptionInterface
+ */
+ public function validatePrintJob(IppPrinter $printer, IppPrintFile $file): IppResponseInterface
+ {
+ $this->printOperation ??= $this->printOperationFactory->create($this->server, $this->httpClient, $this->parserFactory, $this->logger);
+
+ return $this->printOperation->print($printer, $file, true);
+ }
}
diff --git a/src/Operations/PrintOperation.php b/src/Operations/PrintOperation.php
index c152935..186890d 100644
--- a/src/Operations/PrintOperation.php
+++ b/src/Operations/PrintOperation.php
@@ -37,11 +37,11 @@ public function __construct(
/**
* @throws ClientExceptionInterface
*/
- public function print(IppPrinter $printer, IppPrintFile $file): IppResponseInterface
+ public function print(IppPrinter $printer, IppPrintFile $file, bool $dryRun = false): IppResponseInterface
{
$printerUri = $this->server->getUri() . '/printers/' . $printer->getHostname();
- $operation = new IppOperation(IppOperationEnum::PrintJob);
+ $operation = new IppOperation($dryRun ? IppOperationEnum::ValidateJob : IppOperationEnum::PrintJob);
$operation->addOperationAttribute(new IppAttribute(IppTypeEnum::Charset, 'attributes-charset', 'utf-8'));
$operation->addOperationAttribute(new IppAttribute(IppTypeEnum::NaturalLanguage, 'attributes-natural-language', 'en'));
$operation->addOperationAttribute(new IppAttribute(IppTypeEnum::Uri, 'printer-uri', $printerUri));
diff --git a/src/Protocol/Response/IppResponseState.php b/src/Protocol/Response/IppResponseState.php
index a014a3e..523cfab 100644
--- a/src/Protocol/Response/IppResponseState.php
+++ b/src/Protocol/Response/IppResponseState.php
@@ -4,7 +4,7 @@
namespace DR\Ipp\Protocol\Response;
-use DateTime;
+use DateTimeImmutable;
use DR\Ipp\Entity\IppResolution;
use DR\Ipp\Enum\IppTypeEnum;
use DR\Utils\Assert;
@@ -63,7 +63,7 @@ private function consumeBytes(string $unpack, int $byteCount): int|string|array
return str_contains($unpack, '/') ? $this->unpackArray($unpack, $byteCount) : $this->unpackSingleValue($unpack, $byteCount);
}
- private function consumeDateTime(int $length): DateTime
+ private function consumeDateTime(int $length): DateTimeImmutable
{
// Datetime in rfc2579 format: https://datatracker.ietf.org/doc/html/rfc2579
/** @var array{year: int, month: int, day: int, hour: int, min: int, sec: int, int, tz: string, tzhour:int, tzmin: int} $dateTime */
@@ -73,7 +73,7 @@ private function consumeDateTime(int $length): DateTime
$time = $dateTime['hour'] . ':' . sprintf('%02d', $dateTime['min']) . ':' . sprintf('%02d', $dateTime['sec']);
$timeZone = $dateTime['tz'] . sprintf('%02d', $dateTime['tzhour']) . sprintf('%02d', $dateTime['tzmin']);
- $converted = DateTime::createFromFormat('Y-n-j G:i:sO', $date . ' ' . $time . $timeZone);
+ $converted = DateTimeImmutable::createFromFormat('Y-n-j G:i:sO', $date . ' ' . $time . $timeZone);
if ($converted === false) {
throw new RuntimeException('Invalid DateTime in IPP attribute');
}
diff --git a/tests/Unit/Client/CupsIppHttpClientTest.php b/tests/Unit/Client/CupsIppHttpClientTest.php
index 8d3ec10..a501edf 100644
--- a/tests/Unit/Client/CupsIppHttpClientTest.php
+++ b/tests/Unit/Client/CupsIppHttpClientTest.php
@@ -105,7 +105,7 @@ public function testException(): void
$server->expects($this->once())->method('getUri')->willReturn('https://cups');
$response = $this->createMock(ResponseInterface::class);
- $response->expects($this->exactly(4))->method('getStatusCode')->willReturn(500);
+ $response->expects($this->exactly(3))->method('getStatusCode')->willReturn(500);
$response->expects($this->once())->method('getReasonPhrase')->willReturn('Internal server error');
$response->expects($this->once())->method('getBody')->willReturn($this->createMock(StreamInterface::class));
diff --git a/tests/Unit/Factory/IppJobFactoryTest.php b/tests/Unit/Factory/IppJobFactoryTest.php
index 91f9070..210571f 100644
--- a/tests/Unit/Factory/IppJobFactoryTest.php
+++ b/tests/Unit/Factory/IppJobFactoryTest.php
@@ -4,7 +4,7 @@
namespace DR\Ipp\Tests\Unit\Factory;
-use DateTime;
+use DateTimeImmutable;
use DR\Ipp\Enum\IppTypeEnum;
use DR\Ipp\Enum\JobStateEnum;
use DR\Ipp\Enum\JobStateReasonEnum;
@@ -41,7 +41,7 @@ public function testCreateMinimal(): void
public function testCreate(): void
{
- $date = new DateTime();
+ $date = new DateTimeImmutable();
$attributes = [];
$attributes['job-id'] = new IppAttribute(IppTypeEnum::Int, 'job-id', 1);
diff --git a/tests/Unit/Factory/IppPrinterFactoryTest.php b/tests/Unit/Factory/IppPrinterFactoryTest.php
index a134821..655ba4f 100644
--- a/tests/Unit/Factory/IppPrinterFactoryTest.php
+++ b/tests/Unit/Factory/IppPrinterFactoryTest.php
@@ -4,9 +4,11 @@
namespace DR\Ipp\Tests\Unit\Factory;
+use DateTimeImmutable;
+use DR\Ipp\Enum\AuthenticationSupportEnum;
use DR\Ipp\Enum\IppTypeEnum;
use DR\Ipp\Enum\PrinterStateEnum;
-use DR\Ipp\Enum\PrinterStateReasonEnum;
+use DR\Ipp\Enum\PrintQualityEnum;
use DR\Ipp\Factory\IppPrinterFactory;
use DR\Ipp\Protocol\IppAttribute;
use PHPUnit\Framework\Attributes\CoversClass;
@@ -23,13 +25,24 @@ public function testCreateNull(): void
public function testCreate(): void
{
- $attributes = [];
- $attributes['printer-name'] = new IppAttribute(IppTypeEnum::Keyword, 'printer-name', 'unit');
- $attributes['printer-location'] = new IppAttribute(IppTypeEnum::Keyword, 'printer-location', 'test');
- $attributes['device-uri'] = new IppAttribute(IppTypeEnum::Keyword, 'device-uri', 'foo');
- $attributes['printer-state'] = new IppAttribute(IppTypeEnum::Int, 'printer-state', PrinterStateEnum::Idle->value);
- $attributes['printer-state-reasons'] = new IppAttribute(IppTypeEnum::Int, 'printer-state', PrinterStateReasonEnum::Paused->value);
- $attributes['printer-make-and-model'] = new IppAttribute(IppTypeEnum::Keyword, 'printer-make-and-model', 'baz');
+ $attributes = [];
+ $attributes['printer-name'] = new IppAttribute(IppTypeEnum::Keyword, 'printer-name', 'unit');
+ $attributes['printer-location'] = new IppAttribute(IppTypeEnum::Keyword, 'printer-location', 'test');
+ $attributes['device-uri'] = new IppAttribute(IppTypeEnum::Keyword, 'device-uri', 'foo');
+ $attributes['printer-state'] = new IppAttribute(IppTypeEnum::Int, 'printer-state', PrinterStateEnum::Idle->value);
+ $attributes['printer-state-reasons'] = new IppAttribute(IppTypeEnum::Int, 'printer-state', 'paused');
+ $attributes['printer-make-and-model'] = new IppAttribute(IppTypeEnum::Keyword, 'printer-make-and-model', 'baz');
+ $attributes['printer-up-time'] = new IppAttribute(IppTypeEnum::Int, 'printer-up-time', 0);
+ $attributes['uri-authentication-supported'] = new IppAttribute(
+ IppTypeEnum::Keyword,
+ 'uri-authentication-supported',
+ AuthenticationSupportEnum::None->value,
+ );
+ $attributes['print-quality-supported'] = new IppAttribute(
+ IppTypeEnum::Keyword,
+ 'print-quality-supported',
+ [PrintQualityEnum::Normal->value, PrintQualityEnum::High->value],
+ );
$factory = new IppPrinterFactory();
$printer = $factory->create($attributes);
@@ -38,7 +51,10 @@ public function testCreate(): void
static::assertSame('test', $printer->getLocation());
static::assertSame('foo', $printer->getDeviceUri());
static::assertSame(PrinterStateEnum::Idle, $printer->getPrinterState());
- static::assertSame(PrinterStateReasonEnum::Paused, $printer->getPrinterStateReason());
+ static::assertSame('paused', $printer->getPrinterStateReason());
static::assertSame('baz', $printer->getPrinterType());
+ static::assertSame([AuthenticationSupportEnum::None], $printer->getUriAuthSupported());
+ static::assertSame([PrintQualityEnum::Normal, PrintQualityEnum::High], $printer->getPrintQualitiesSupported());
+ static::assertSame((new DateTimeImmutable('@0'))->getTimeStamp(), $printer->getUpSince()?->getTimeStamp());
}
}
diff --git a/tests/Unit/IppTest.php b/tests/Unit/IppTest.php
index a25c000..9cfe3fd 100644
--- a/tests/Unit/IppTest.php
+++ b/tests/Unit/IppTest.php
@@ -113,6 +113,18 @@ public function testCancelJob(): void
$this->ipp->cancelJob($job);
}
+ public function testValidatePrintJob(): void
+ {
+ $printer = $this->createMock(IppPrinter::class);
+ $file = $this->createMock(IppPrintFile::class);
+
+ $mock = $this->createMock(PrintOperation::class);
+ $this->setPrivateProperty($this->ipp, 'printOperation', $mock);
+
+ $mock->expects($this->once())->method('print')->with($printer, $file, true);
+ $this->ipp->validatePrintJob($printer, $file);
+ }
+
/**
* @param class-string|object $obj
*
diff --git a/tests/Unit/Protocol/Response/IppResponseStateTest.php b/tests/Unit/Protocol/Response/IppResponseStateTest.php
index 745fad8..430328b 100644
--- a/tests/Unit/Protocol/Response/IppResponseStateTest.php
+++ b/tests/Unit/Protocol/Response/IppResponseStateTest.php
@@ -4,7 +4,7 @@
namespace DR\Ipp\Tests\Unit\Protocol\Response;
-use DateTime;
+use DateTimeImmutable;
use DR\Ipp\Entity\IppResolution;
use DR\Ipp\Enum\IppTypeEnum;
use DR\Ipp\Protocol\Response\IppResponseState;
@@ -50,7 +50,7 @@ public function testConsumeBool(): void
public function testConsumeDate(): void
{
- $expected = new DateTime('1990-02-03T14:01:02.000000+0100');
+ $expected = new DateTimeImmutable('1990-02-03T14:01:02.000000+0100');
$state = new IppResponseState(pack('nc6ac2', 1990, 2, 3, 14, 01, 02, 0, '+', 1, 0));
static::assertEquals($expected, $state->consume(11, IppTypeEnum::DateTime));