Form alanı değerlerini doğrulayın ve etkileşimli durumu koruyarak düzleştirin
Bir bakışta
“Bir bakışta” başlıklı bölümÜretim ortamındaki bir form hattı, ham girdiyi genellikle doğrudan düzleştirmez. Önce her değeri doğrularsınız. Sonra hangi alanların kalıcı hale geleceğine, hangilerinin düzenlenebilir kalacağına karar verirsiniz. NextPDF core, bunun için iki yapı taşı sağlar: alanı oluştururken değerini belgeye yazan HasFormFields oluşturma trait’i ve her alanı statik sayfa grafiklerine işleyerek etkileşimli formu kaldıran flattenForms().
Bu tarif, core’un özellikle uygulama katmanına bıraktığı doğrulama adımını bu iki yapı taşıyla birleştirir. Şunları yapacaksınız:
- Herhangi bir alan oluşturmadan önce bir değer eşlemesini alan başına kurallara göre doğrulayın; böylece geçersiz bir değer belgeye asla ulaşmaz.
- Tek bir doğrulanmış veri kümesi oluşturun, ardından bunu iki kez işleyin — bir kez düzleştirilmiş olarak (kilitli, salt okunur bir kopya), bir kez de etkileşimli olarak (düzenlenebilir bir kopya) — böylece aynı alan durumu her iki çıktıya da taşınır.
Ön koşullar: çalışan bir NextPDF core kurulumu (composer require nextpdf/core) ve bu tarifin bir araya getirdiği alan oluşturma ile düzleştirme mekanizmalarını anlatan PDF formu oluşturma ve ön doldurma ve Form alanlarını düzleştirme konularını okumuş olmanız.
Kapsam sınırı. Core’un
flattenForms()işlemi belgenin tamamına uygulanır: ya tüm alanları düzleştirir ya da hiçbirini. Core form API’sinde herkese açık, alan başına çalışan bir düzleştirme anahtarı veya yerleşik değer doğrulayıcı yoktur. Bu nedenle “bazılarını düzleştir, diğerlerini düzenlenebilir tut” işlemi uygulama katmanında gerçekleşir: bir kez doğrulayın, ardından aynı doğrulanmış veri kümesini iki belgeye işleyin. Bu tarif söz konusu deseni belgeler; alan başına çalışan bir core yöntemi varsaymaz.
Kurulum
“Kurulum” başlıklı bölümcomposer require nextpdf/coreEk bir uzantıya ihtiyacınız yoktur. Form oluşturma trait’i de düzleştirici de core ile birlikte gelir.
Kavramsal genel bakış
“Kavramsal genel bakış” başlıklı bölümBir Acrobat form (AcroForm) alanı, geçerli değerini alan sözlüğünün V girdisinde saklar. flattenForms() her alanın V değerini okur ve bunu alanın ait olduğu sayfanın içerik akışına işler — metin alanları BT ... Tj ... ET metnine, onay kutuları ve radyo düğmeleri çizilmiş yollara dönüşür, seçim alanları ise seçili öğelerini işler. Ardından /AcroForm katalog girdisini kaldırır. Sonuç, etkileşimli olmayan bir formdur: alanların, herhangi bir okuyucuda aynı şekilde görüntülenen ve form doldurma yeteneği gerektirmeyen statik bir gösterimi (ISO 32000-2 12.7).
Üretim desenini iki gerçek belirler:
-
Core, alan değerlerini doğrulamaz. Her oluşturma yöntemi (
textField(),comboBox(),checkBox()ve diğerleri), ilettiğiniz değeri doğrudanViçine yazar. E-posta biçimi, izin verilen seçeneklere üyelik ve zorunlu alanların mevcudiyeti uygulamanın sorumluluğundadır. Oluşturmadan önce doğrulayın ve içine geçersiz bir değer gömülmüş bir belge yaymak yerine bir kural ihlalinde hızlıca başarısız olun. -
Düzleştirme geri alınamaz ve belge geneline uygulanır.
flattenForms()vesave()çağrısından sonra alanlar statik grafiklere dönüşür. Düzenlenebilir bir kopya tutmak için düzleştirmeyi geri almaya çalışmazsınız — doğrulanmış veri kümesiniflattenForms()çağırmadan ikinci kez işlersiniz. Her iki kopya da aynı doğrulanmış değerlerden başlar; böylece kilitli kopya ve düzenlenebilir kopya aynı alan durumunu taşır.
Yeniden üretilebilirlik profili structuraldır: her belge, iki çalıştırma karşılaştırılmadan önce son geçişte normalleştirilen bir treyler /ID dizisi taşır.
API yüzeyi
“API yüzeyi” başlıklı bölümNextPDF\Core\Document (NextPDF\Core\Concerns\HasFormFields aracılığıyla):
textField(string $name, float $x, float $y, float $w, float $h, string $default = '', array $options = []): static— değeridefaultile verilen bir metin alanı oluşturur.comboBox(string $name, float $x, float $y, float $w, float $h, array $items, string $selected = ''): static— seçili öğesiselectedile verilen bir açılır liste oluşturur.checkBox(string $name, float $x, float $y, float $size, bool $checked = false): static— durumucheckedile verilen bir onay kutusu oluşturur.flattenForms(): static— her alanın değerini statik sayfa içeriğine işler ve AcroForm’u kaldırır. Hiç alan yoksa işlemsizdir. Dahili olarakNextPDF\Form\FormFlattenersınıfına devreder.
NextPDF\Core\Concerns\HasOutput:
save(string $path): void— PDF’yi oluşturur ve yazar. Çıktı yolu bir akış sarmalayıcı olduğunda, bir boş bayt içerdiğinde veya var olmayan bir üst dizine işaret ettiğindeNextPDF\Exception\InvalidConfigExceptionfırlatır.
Aşağıdaki örneklerdeki doğrulayıcı, bir core sembolü değil, size ait uygulama kodudur. Core’un bir değer doğrulama API’si yoktur; doğrulama adımının burada açıkça yer almasının nedeni budur.
Kod örneği — hızlı başlangıç
“Kod örneği — hızlı başlangıç” başlıklı bölümBu minimal akış, bir değer eşlemesini doğrular, bu eşlemeden üç alan oluşturur, ardından kilitli bir kopyayı düzleştirip kaydeder.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
/** @var array<string, string> $input Untrusted value map (e.g. from a request). */$input = [ 'full_name' => 'Ada Lovelace', 'country' => 'Taiwan',];
$allowedCountries = ['United Kingdom', 'Taiwan', 'Japan'];
// Validate before authoring: core writes whatever you pass, so reject early.if (trim($input['full_name']) === '') { throw new InvalidArgumentException('full_name must not be empty.');}if (filter_var($input['email'], FILTER_VALIDATE_EMAIL) === false) { throw new InvalidArgumentException('email is not a valid address.');}if (!in_array($input['country'], $allowedCountries, true)) { throw new InvalidArgumentException('country is not an allowed option.');}
$doc = Document::createStandalone();$doc->setTitle('Validated Form (locked copy)');$doc->addPage();
$doc->textField(name: 'full_name', x: 20, y: 30, w: 90, h: 8, default: $input['full_name']);$doc->textField(name: 'email', x: 20, y: 45, w: 90, h: 8, default: $input['email']);$doc->comboBox( name: 'country', x: 20, y: 60, w: 90, h: 8, items: $allowedCountries, selected: $input['country'],);
// Whole-document flatten: every field becomes static graphics.$doc->flattenForms();
$doc->save(__DIR__ . '/registration-locked.pdf');echo "Wrote registration-locked.pdf\n";Kod örneği — üretim
“Kod örneği — üretim” başlıklı bölümÜretim akışı, doğrulamayı işleme adımından ayırır. Türlenmiş bir FieldRuleSet, değer eşlemesini bir kez doğrular ve doğrulanmış bir veri kümesi döndürür. Tek bir renderForm() yardımcısı alanları oluşturur ve iki kez çalışır — kilitli kopya için düzleştirmeyle, düzenlenebilir kopya için düzleştirmesiz. Her iki kopya da aynı doğrulanmış değerlerden gelir; böylece etkileşimli durum onlara taşınır. Üst seviye kod, çıktı yollarını NEXTPDF_COOKBOOK_LOCKED_OUTPUT ve NEXTPDF_COOKBOOK_EDITABLE_OUTPUT değişkenlerinden okur.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;use NextPDF\Exception\InvalidConfigException;
/** * A single validated form record. Immutable: once constructed, every value has * already passed the rule set, so the rendering step cannot re-introduce a bad * value. This is the "validate once, render many" boundary. */final readonly class ValidatedRegistration{ /** * @param non-empty-string $fullName * @param non-empty-string $email * @param non-empty-string $country */ public function __construct( public string $fullName, public string $email, public string $country, public bool $newsletter, ) {}}
/** * Application-layer field validation. Core performs no value validation, so the * rules live here. Each method throws on the first violation; the caller maps * the exception to an HTTP 422 or a user-facing message. */final class FieldRuleSet{ /** @var list<non-empty-string> */ private const array ALLOWED_COUNTRIES = ['United Kingdom', 'Taiwan', 'Japan', 'Germany'];
/** * @param array<string, string|bool> $input Untrusted value map. * * @throws InvalidArgumentException When any field fails its rule. */ public function validate(array $input): ValidatedRegistration { $fullName = $this->requireNonEmpty($input, 'full_name'); $email = $this->requireEmail($input, 'email'); $country = $this->requireAllowed($input, 'country', self::ALLOWED_COUNTRIES);
$newsletter = $input['newsletter'] ?? false; if (!is_bool($newsletter)) { throw new InvalidArgumentException('newsletter must be a boolean.'); }
return new ValidatedRegistration( fullName: $fullName, email: $email, country: $country, newsletter: $newsletter, ); }
/** * @param array<string, string|bool> $input * * @return non-empty-string * * @throws InvalidArgumentException */ private function requireNonEmpty(array $input, string $key): string { $value = $input[$key] ?? ''; if (!is_string($value) || trim($value) === '') { throw new InvalidArgumentException(sprintf('%s must not be empty.', $key)); }
return $value; }
/** * @param array<string, string|bool> $input * * @return non-empty-string * * @throws InvalidArgumentException */ private function requireEmail(array $input, string $key): string { $value = $input[$key] ?? ''; if (!is_string($value) || $value === '' || filter_var($value, FILTER_VALIDATE_EMAIL) === false) { throw new InvalidArgumentException(sprintf('%s is not a valid email address.', $key)); }
return $value; }
/** * @param array<string, string|bool> $input * @param list<non-empty-string> $allowed * * @return non-empty-string * * @throws InvalidArgumentException */ private function requireAllowed(array $input, string $key, array $allowed): string { $value = $input[$key] ?? ''; if (!is_string($value) || !in_array($value, $allowed, true)) { throw new InvalidArgumentException(sprintf('%s is not an allowed option.', $key)); }
return $value; }
/** @return list<non-empty-string> */ public function allowedCountries(): array { return self::ALLOWED_COUNTRIES; }}
/** * Author the same field layout from one validated record. When $flatten is * true, every field is baked into static page content and the AcroForm is * dropped; when false, the fields stay interactive and editable. Both paths * start from identical values, preserving the form's state across copies. */function renderForm(ValidatedRegistration $record, FieldRuleSet $rules, bool $flatten): Document{ $doc = Document::createStandalone(); $doc->setTitle($flatten ? 'Customer Registration (locked)' : 'Customer Registration (editable)'); $doc->addPage();
$doc->setFont('helvetica', 'B', 18); $doc->cell(0, 12, 'Customer Registration', newLine: true); $doc->ln(4);
$leftMargin = 15.0; $fieldX = 70.0; $fieldW = 120.0; $fieldH = 8.0; $rowSpacing = 12.0; $y = 40.0;
$doc->setFont('helvetica', '', 10);
$doc->setXY($leftMargin, $y); $doc->cell(50, $fieldH, 'Full Name:'); $doc->textField(name: 'full_name', x: $fieldX, y: $y, w: $fieldW, h: $fieldH, default: $record->fullName); $y += $rowSpacing;
$doc->setXY($leftMargin, $y); $doc->cell(50, $fieldH, 'Email:'); $doc->textField(name: 'email', x: $fieldX, y: $y, w: $fieldW, h: $fieldH, default: $record->email); $y += $rowSpacing;
$doc->setXY($leftMargin, $y); $doc->cell(50, $fieldH, 'Country:'); $doc->comboBox( name: 'country', x: $fieldX, y: $y, w: $fieldW, h: $fieldH, items: $rules->allowedCountries(), selected: $record->country, ); $y += $rowSpacing;
$doc->setXY($leftMargin, $y); $doc->cell(50, $fieldH, 'Newsletter:'); $doc->checkBox(name: 'newsletter', x: $fieldX, y: $y, size: 5, checked: $record->newsletter);
if ($flatten) { // Whole-document flatten: locked, read-only copy. $doc->flattenForms(); }
return $doc;}
/** @var array<string, string|bool> $input Untrusted value map (request payload). */$input = [ 'full_name' => 'Ada Lovelace', 'country' => 'Taiwan', 'newsletter' => true,];
$rules = new FieldRuleSet();
try { // Validate once. A violation aborts before any document is built. $record = $rules->validate($input);
// Render the validated dataset twice: locked, then editable. $locked = renderForm($record, $rules, flatten: true); $editable = renderForm($record, $rules, flatten: false);
$lockedPath = getenv('NEXTPDF_COOKBOOK_LOCKED_OUTPUT') ?: __DIR__ . '/registration-locked.pdf'; $editablePath = getenv('NEXTPDF_COOKBOOK_EDITABLE_OUTPUT') ?: __DIR__ . '/registration-editable.pdf';
$locked->save($lockedPath); $editable->save($editablePath);
echo "Wrote locked and editable registration copies\n";} catch (InvalidArgumentException $e) { // Validation failure: a bad value never reached the document. fwrite(STDERR, 'Form validation failed: ' . $e->getMessage() . "\n"); exit(1);} catch (InvalidConfigException $e) { // Output failure: bad path, missing directory, or stream wrapper. fwrite(STDERR, sprintf( 'PDF save failed for key "%s": %s' . "\n", $e->getConfigKey(), $e->getMessage(), )); exit(1);}Beklenen çıktı:
Wrote locked and editable registration copiesKilitli kopya, etkileşimli alanlar olmadan açılır — değerler statik grafiklere dönüşmüştür. Düzenlenebilir kopya, aynı değerler ön doldurulmuş halde açılır ve her alan düzenlenebilir kalır. Her ikisi de tek bir doğrulanmış veri kümesini yansıtır.
Sınır durumlar & tuzaklar
“Sınır durumlar & tuzaklar” başlıklı bölüm- Oluşturmadan önce doğrulayın, sonrasında değil. Oluşturma yöntemleri, ilettiğiniz değeri
Viçine olduğu gibi yazar. Core’da hiçbir kanca, hatalı biçimli bir değerisave()sırasında reddetmez; bu nedenle doğrulamayı atlamış bir değer, kurtarma yolu olmadan düzleştirilmiş kopyaya gömülür. flattenForms()ya hep ya hiçtir. Belgedeki tüm alanları düzleştirir. Bazı alanları düzenlenebilir tutmak için, üretim örneğindeki gibi düzleştirme çağrısı olmadan ikinci bir belge işleyin — core API’sinde alan başına bir anahtar beklemeyin.- Çağrı sırası. Önce her alanı oluşturun, ardından
flattenForms(), ardındansave()çağırın. Hiç alan yokkenflattenForms()çağırmak güvenli biçimde hiçbir işlem yapmaz; bu yöntemisave()sonrasında çağırmak, zaten yazılmış baytları etkilemez. - Alan adları benzersiz olmalıdır. Aynı adı paylaşan iki alan, uyumlu okuyucularda paylaşılan tek bir değere sahip tek bir mantıksal alana dönüşür. Adlar veriden türetiliyorsa benzersiz olduklarını doğrulayın.
- Düzleştirmede onay kutusunun doğru kabul edilen değeri. Düzleştirilmiş bir onay kutusu, değeri
Yes,On,1veyatrueolduğunda onay işaretini çizer; boş veyaOffbir değer yalnızca kutuyu çizer. İşlenen durumun doğrulanmış değerinizle eşleşmesi içincheckBox()yöntemine gerçek bir boole değeri iletin. - İmza alanları asla düzleştirilmez. Bir
/Sigalanının yüzeyi, yeniden işlenebilir bir değer değil, imza yükünden üretilen görünümdür; bu nedenle düzleştirici onu atlar. İmzalamadan önce düzleştirin, sonrasında asla. - Düzenlenebilir kopya hâlâ düzenlenebilirdir. Durumu düzenlenebilir kopyada korumak, alıcının onu değiştirebileceği anlamına gelir. Kilitli kopyayı yetkili kayıt, düzenlenebilir kopyayı ise çalışma taslağı olarak değerlendirin.
Performans
“Performans” başlıklı bölümDoğrulama, alan sayısıyla doğrusaldır ve her veri kümesi için bir kez çalışır. Her işleme geçişi, alan başına bir widget açıklaması ve bir görünüm oluşturur. Düzleştirme adımı, alan başına sınırlı bir içerik bloğu ekler. Veri kümesini iki kez işlemek, belge başına maliyeti kabaca ikiye katlar; bu da birkaç yüz alanlı formlar için 1500 ms / 64 MB bütçesinin oldukça içinde kalır. Yalnızca tek bir çıktıya ihtiyacınız varsa, bir kez işleyin ve ikinci geçişi atlayın.
Güvenlik notları
“Güvenlik notları” başlıklı bölüm- Güvenilmeyen girdiyi sınırda doğrulayın. E-posta biçimi, izin verilen seçeneklere üyelik ve zorunlu alanların mevcudiyeti, core bunları zorlamadığı için uygulama kodunda uygulanır. Güvenilmeyen girdiden türetilen herhangi bir değeri, bir alana ulaşmadan önce kaçışlayın veya normalleştirin; çünkü core onu belgeye olduğu gibi yazar.
- Düzleştirme bir erişim denetimi değildir. Düzleştirilmiş bir değer normal bir okuyucuda düzenlenemez, ancak sayfa içeriğinde görünür kalır ve herhangi bir metin aracıyla çıkarılabilir. Düzleştirmeyi bir redaksiyon ya da hassas değerlerin korunması olarak değerlendirmeyin.
- Düzenlenebilir kopya aynı verileri taşır. Onu yalnızca bu değerleri görmeye ve değiştirmeye yetkili taraflara dağıtın. İçerik hassas olduğunda, her iki kopyayı da İzinlerle şifreleme ile birleştirin ve orada açıklanan okuyucu iş birliğine bağlı uyarıyı dikkate alın: izin bitleri okuma kısıtlamalarını zorlamaz.
- Kapalı başarısız olun. Üretim örneği, kısmi veya geçersiz bir belge yazmak yerine bir doğrulama veya çıktı hatasında sıfırdan farklı bir kodla çıkar. Bu istisnaları asla yutmayın.
Uygunluk
“Uygunluk” başlıklı bölüm| İfade | Spesifikasyon | Madde | reference_id |
|---|---|---|---|
| Düzleştirilmiş bir form, alanların etkileşimli olmayan (statik) bir gösterimidir. | ISO 32000-2 | 12.7 |
NextPDF, atıfta bulunulan maddede açıklanan statik yapıyı üretir; genel ISO 32000-2 uygunluğu iddia etmez. Bu tarifteki doğrulama kuralları, standardın bir uygunluk gereksinimi değil, uygulama politikasıdır.
Ayrıca bakınız
“Ayrıca bakınız” başlıklı bölüm- PDF formu oluşturma ve ön doldurma — alanları oluşturun ve başlangıç değerlerini ayarlayın.
- Form alanlarını düzleştirme — bu tarifin üzerine kurulduğu belge geneline uygulanan düzleştirme.
- NextPDF istisna hiyerarşisiyle hataları işleme — hataları uygun ayrıntı düzeyinde yakalayın.
- İzinlerle şifreleme — form verisi hassas olduğunda gizlilik ekleyin.
- Form modülü — form alanı referansı.