id = (int) $data['id']; $version->productId = (int) $data['product_id']; $version->version = $data['version']; $version->majorVersion = (int) $data['major_version']; $version->minorVersion = (int) $data['minor_version']; $version->patchVersion = (int) $data['patch_version']; $version->releaseNotes = $data['release_notes'] ?: null; $version->downloadUrl = $data['download_url'] ?: null; $version->attachmentId = !empty($data['attachment_id']) ? (int) $data['attachment_id'] : null; $version->fileHash = $data['file_hash'] ?? null; $version->downloadCount = (int) ($data['download_count'] ?? 0); $version->isActive = (bool) $data['is_active']; $version->releasedAt = new \DateTimeImmutable($data['released_at']); $version->createdAt = new \DateTimeImmutable($data['created_at']); return $version; } /** * Parse version string into components */ public static function parseVersion(string $versionString): array { $parts = explode('.', $versionString); return [ 'major' => (int) ($parts[0] ?? 0), 'minor' => (int) ($parts[1] ?? 0), 'patch' => (int) ($parts[2] ?? 0), ]; } /** * Extract version from filename * * Supports patterns like: * - product-name-v1.2.3.zip * - product-name-1.2.3.zip * - product_v1.2.3.zip * - v1.2.3.zip */ public static function extractVersionFromFilename(string $filename): ?string { // Remove extension $basename = pathinfo($filename, PATHINFO_FILENAME); // Try various patterns $patterns = [ '/[_-]v?(\d+\.\d+\.\d+)$/i', // ends with -v1.2.3 or -1.2.3 '/[_-]v?(\d+\.\d+\.\d+)[_-]/i', // contains -v1.2.3- or -1.2.3- '/^v?(\d+\.\d+\.\d+)$/i', // just version number ]; foreach ($patterns as $pattern) { if (preg_match($pattern, $basename, $matches)) { return $matches[1]; } } return null; } public function getId(): int { return $this->id; } public function getProductId(): int { return $this->productId; } public function getVersion(): string { return $this->version; } public function getMajorVersion(): int { return $this->majorVersion; } public function getMinorVersion(): int { return $this->minorVersion; } public function getPatchVersion(): int { return $this->patchVersion; } public function getReleaseNotes(): ?string { return $this->releaseNotes; } public function getDownloadUrl(): ?string { return $this->downloadUrl; } public function getAttachmentId(): ?int { return $this->attachmentId; } public function getFileHash(): ?string { return $this->fileHash; } public function getDownloadCount(): int { return $this->downloadCount; } /** * Get the download URL from attachment */ public function getEffectiveDownloadUrl(): ?string { if ($this->attachmentId) { return wp_get_attachment_url($this->attachmentId) ?: null; } return null; } /** * Get the filename for download */ public function getDownloadFilename(): ?string { if ($this->attachmentId) { return wp_basename(get_attached_file($this->attachmentId) ?: ''); } return null; } public function isActive(): bool { return $this->isActive; } public function getReleasedAt(): \DateTimeInterface { return $this->releasedAt; } public function getCreatedAt(): \DateTimeInterface { return $this->createdAt; } /** * Convert to array */ public function toArray(): array { return [ 'id' => $this->id, 'product_id' => $this->productId, 'version' => $this->version, 'major_version' => $this->majorVersion, 'minor_version' => $this->minorVersion, 'patch_version' => $this->patchVersion, 'release_notes' => $this->releaseNotes, 'download_url' => $this->downloadUrl, 'attachment_id' => $this->attachmentId, 'file_hash' => $this->fileHash, 'download_count' => $this->downloadCount, 'is_active' => $this->isActive, 'released_at' => $this->releasedAt->format('Y-m-d H:i:s'), 'created_at' => $this->createdAt->format('Y-m-d H:i:s'), ]; } }