เน็ตเต้ สคีมา
ไลบรารีที่ใช้งานได้จริงสำหรับการตรวจสอบความถูกต้องและการทำให้โครงสร้างข้อมูลเป็นมาตรฐานเทียบกับสคีมาที่กำหนดด้วย API ที่ชาญฉลาดและเข้าใจง่าย
เอกสารสามารถพบได้บนเว็บไซต์
การติดตั้ง:
composer require nette/schema
ต้องใช้ PHP เวอร์ชัน 8.1 และรองรับ PHP สูงสุด 8.4
คุณชอบ Nette Schema ไหม? คุณกำลังรอคอยคุณสมบัติใหม่หรือไม่?
ขอบคุณ!
ในตัวแปร $schema
เรามีสคีมาการตรวจสอบ (ความหมายที่แท้จริงและวิธีสร้างเราจะกล่าวในภายหลัง) และในตัวแปร $data
เรามีโครงสร้างข้อมูลที่เราต้องการตรวจสอบและทำให้เป็นมาตรฐาน ตัวอย่างเช่น ข้อมูลที่ผู้ใช้ส่งผ่าน API ไฟล์การกำหนดค่า ฯลฯ
งานได้รับการจัดการโดยคลาส NetteSchemaProcessor ซึ่งประมวลผลอินพุตและส่งคืนข้อมูลที่ทำให้เป็นมาตรฐานหรือส่งข้อยกเว้น NetteSchemaValidationException จากข้อผิดพลาด
$ processor = new Nette Schema Processor ;
try {
$ normalized = $ processor -> process ( $ schema , $ data );
} catch ( Nette Schema ValidationException $ e ) {
echo ' Data is invalid: ' . $ e -> getMessage ();
}
วิธีการ $e->getMessages()
ส่งคืนอาร์เรย์ของสตริงข้อความทั้งหมด และ $e->getMessageObjects()
ส่งคืนข้อความทั้งหมดเป็นวัตถุ NetteSchemaMessage
และตอนนี้เรามาสร้างสคีมากันดีกว่า คลาส NetteSchemaExpect ใช้เพื่อกำหนด จริงๆ แล้วเรากำหนดความคาดหวังว่าข้อมูลควรมีลักษณะอย่างไร สมมติว่าข้อมูลที่ป้อนต้องเป็นโครงสร้าง (เช่นอาร์เรย์) ที่มีองค์ประกอบ processRefund
ประเภทบูลและ refundAmount
ประเภท int
use Nette Schema Expect ;
$ schema = Expect:: structure ([
' processRefund ' => Expect:: bool (),
' refundAmount ' => Expect:: int (),
]);
เราเชื่อว่าคำจำกัดความของสคีมานั้นชัดเจน แม้ว่าคุณจะเห็นเป็นครั้งแรกก็ตาม
ให้ส่งข้อมูลต่อไปนี้เพื่อตรวจสอบความถูกต้อง:
$ data = [
' processRefund ' => true ,
' refundAmount ' => 17 ,
];
$ normalized = $ processor -> process ( $ schema , $ data ); // OK, it passes
ผลลัพธ์ เช่น ค่า $normalized
คืออ็อบเจ็กต์ stdClass
หากเราต้องการให้เอาท์พุตเป็นอาร์เรย์ เราจะเพิ่มการส่งไปยังสคีมา Expect::structure([...])->castTo('array')
องค์ประกอบทั้งหมดของโครงสร้างเป็นทางเลือกและมีค่าเริ่มต้น null
ตัวอย่าง:
$ data = [
' refundAmount ' => 17 ,
];
$ normalized = $ processor -> process ( $ schema , $ data ); // OK, it passes
// $normalized = {'processRefund' => null, 'refundAmount' => 17}
ความจริงที่ว่าค่าเริ่มต้นเป็น null
ไม่ได้หมายความว่าจะได้รับการยอมรับในข้อมูลอินพุต 'processRefund' => null
ไม่ ข้อมูลอินพุตต้องเป็นบูลีน กล่าวคือ เฉพาะ true
หรือ false
เท่านั้น เราจะต้องอนุญาตอย่างชัดเจนให้ null
ผ่าน Expect::bool()->nullable()
รายการสามารถบังคับได้โดยใช้ Expect::bool()->required()
เราเปลี่ยนค่าเริ่มต้นเป็น false
โดยใช้ Expect::bool()->default(false)
หรือใช้ Expect::bool(false)
ในไม่ช้า
แล้วถ้าเราอยากจะยอมรับ 1
และ 0
นอกเหนือจากบูลีนล่ะ? จากนั้นเราจะแสดงรายการค่าที่อนุญาต ซึ่งเราจะทำให้เป็นมาตรฐานบูลีนด้วย:
$ schema = Expect:: structure ([
' processRefund ' => Expect:: anyOf ( true , false , 1 , 0 )-> castTo ( ' bool ' ),
' refundAmount ' => Expect:: int (),
]);
$ normalized = $ processor -> process ( $ schema , $ data );
is_bool ( $ normalized -> processRefund ); // true
ตอนนี้คุณรู้พื้นฐานเกี่ยวกับวิธีการกำหนดสคีมาและลักษณะการทำงานขององค์ประกอบแต่ละส่วนของโครงสร้างแล้ว ตอนนี้เราจะแสดงองค์ประกอบอื่นๆ ทั้งหมดที่สามารถนำมาใช้ในการกำหนดสคีมาได้
ประเภทข้อมูล PHP มาตรฐานทั้งหมดสามารถแสดงรายการได้ในสคีมา:
Expect:: string ( $ default = null )
Expect:: int ( $ default = null )
Expect:: float ( $ default = null )
Expect:: bool ( $ default = null )
Expect::null()
Expect:: array ( $ default = [])
จากนั้นทุกประเภทที่รองรับโดยเครื่องมือตรวจสอบความถูกต้องผ่านทาง Expect::type('scalar')
หรือตัวย่อ Expect::scalar()
นอกจากนี้ยังยอมรับชื่อคลาสหรืออินเทอร์เฟซ เช่น Expect::type('AddressEntity')
คุณยังสามารถใช้สัญลักษณ์สหภาพได้:
Expect:: type ( ' bool|string|array ' )
ค่าเริ่มต้นจะเป็น null
เสมอ ยกเว้น array
และ list
โดยที่เป็นอาร์เรย์ว่าง (รายการคืออาร์เรย์ที่จัดทำดัชนีโดยเรียงคีย์ตัวเลขจากศูนย์จากน้อยไปมาก นั่นคืออาร์เรย์ที่ไม่เชื่อมโยง)
อาร์เรย์มีโครงสร้างที่กว้างเกินไป จะมีประโยชน์มากกว่าในการระบุว่าองค์ประกอบใดบ้างที่สามารถมีได้ ตัวอย่างเช่น อาร์เรย์ที่องค์ประกอบสามารถเป็นสตริงได้เท่านั้น:
$ schema = Expect:: arrayOf ( ' string ' );
$ processor -> process ( $ schema , [ ' hello ' , ' world ' ]); // OK
$ processor -> process ( $ schema , [ ' a ' => ' hello ' , ' b ' => ' world ' ]); // OK
$ processor -> process ( $ schema , [ ' key ' => 123 ]); // ERROR: 123 is not a string
พารามิเตอร์ตัวที่สองสามารถใช้เพื่อระบุคีย์ (ตั้งแต่เวอร์ชัน 1.2):
$ schema = Expect:: arrayOf ( ' string ' , ' int ' );
$ processor -> process ( $ schema , [ ' hello ' , ' world ' ]); // OK
$ processor -> process ( $ schema , [ ' a ' => ' hello ' ]); // ERROR: 'a' is not int
รายการเป็นอาร์เรย์ที่จัดทำดัชนี:
$ schema = Expect:: listOf ( ' string ' );
$ processor -> process ( $ schema , [ ' a ' , ' b ' ]); // OK
$ processor -> process ( $ schema , [ ' a ' , 123 ]); // ERROR: 123 is not a string
$ processor -> process ( $ schema , [ ' key ' => ' a ' ]); // ERROR: is not a list
$ processor -> process ( $ schema , [ 1 => ' a ' , 0 => ' b ' ]); // ERROR: is not a list
พารามิเตอร์ยังสามารถเป็นสคีมาได้ ดังนั้นเราจึงสามารถเขียนได้:
Expect:: arrayOf (Expect:: bool ())
ค่าเริ่มต้นคืออาร์เรย์ว่าง หากคุณระบุค่าเริ่มต้นและเรียก mergeDefaults()
มันจะถูกรวมเข้ากับข้อมูลที่ส่งผ่าน
anyOf()
คือชุดของค่าหรือสคีมาที่สามารถเป็นค่าได้ ต่อไปนี้เป็นวิธีเขียนอาร์เรย์ขององค์ประกอบที่สามารถเป็น 'a'
, true
หรือ null
:
$ schema = Expect:: listOf (
Expect:: anyOf ( ' a ' , true , null ),
);
$ processor -> process ( $ schema , [ ' a ' , true , null , ' a ' ]); // OK
$ processor -> process ( $ schema , [ ' a ' , false ]); // ERROR: false does not belong there
องค์ประกอบการแจงนับยังสามารถเป็นสคีมาได้:
$ schema = Expect:: listOf (
Expect:: anyOf (Expect:: string (), true , null ),
);
$ processor -> process ( $ schema , [ ' foo ' , true , null , ' bar ' ]); // OK
$ processor -> process ( $ schema , [ 123 ]); // ERROR
เมธอด anyOf()
ยอมรับตัวแปรเป็นพารามิเตอร์แต่ละตัว ไม่ใช่เป็นอาร์เรย์ หากต้องการส่งผ่านอาร์เรย์ของค่า ให้ใช้ตัวดำเนินการคลายแพ็ก anyOf(...$variants)
ค่าเริ่มต้นคือ null
ใช้เมธอด firstIsDefault()
เพื่อทำให้องค์ประกอบแรกเป็นค่าเริ่มต้น:
// default is 'hello'
Expect:: anyOf (Expect:: string ( ' hello ' ), true , null )-> firstIsDefault ();
โครงสร้างเป็นวัตถุที่มีคีย์ที่กำหนดไว้ แต่ละคู่ของคีย์ => ค่าเหล่านี้เรียกว่า "คุณสมบัติ":
โครงสร้างยอมรับอาร์เรย์และวัตถุและส่งคืนวัตถุ stdClass
(เว้นแต่คุณจะเปลี่ยนด้วย castTo('array')
ฯลฯ )
โดยดีฟอลต์ คุณสมบัติทั้งหมดเป็นทางเลือกและมีค่าเริ่มต้นเป็น null
คุณสามารถกำหนดคุณสมบัติบังคับได้โดยใช้ required()
:
$ schema = Expect:: structure ([
' required ' => Expect:: string ()-> required (),
' optional ' => Expect:: string (), // the default value is null
]);
$ processor -> process ( $ schema , [ ' optional ' => '' ]);
// ERROR: option 'required' is missing
$ processor -> process ( $ schema , [ ' required ' => ' foo ' ]);
// OK, returns {'required' => 'foo', 'optional' => null}
หากคุณไม่ต้องการส่งออกคุณสมบัติที่มีเพียงค่าเริ่มต้น ให้ใช้ skipDefaults()
:
$ schema = Expect:: structure ([
' required ' => Expect:: string ()-> required (),
' optional ' => Expect:: string (),
])-> skipDefaults ();
$ processor -> process ( $ schema , [ ' required ' => ' foo ' ]);
// OK, returns {'required' => 'foo'}
แม้ว่า null
จะเป็นค่าเริ่มต้นของคุณสมบัติ optional
แต่ก็ไม่ได้รับอนุญาตในข้อมูลอินพุต (ค่าต้องเป็นสตริง) คุณสมบัติที่ยอมรับ null
ถูกกำหนดโดยใช้ nullable()
:
$ schema = Expect:: structure ([
' optional ' => Expect:: string (),
' nullable ' => Expect:: string ()-> nullable (),
]);
$ processor -> process ( $ schema , [ ' optional ' => null ]);
// ERROR: 'optional' expects to be string, null given.
$ processor -> process ( $ schema , [ ' nullable ' => null ]);
// OK, returns {'optional' => null, 'nullable' => null}
ตามค่าเริ่มต้น ไม่สามารถมีรายการพิเศษในข้อมูลอินพุตได้:
$ schema = Expect:: structure ([
' key ' => Expect:: string (),
]);
$ processor -> process ( $ schema , [ ' additional ' => 1 ]);
// ERROR: Unexpected item 'additional'
ซึ่งเราสามารถเปลี่ยนแปลงได้ด้วย otherItems()
ในฐานะพารามิเตอร์ เราจะระบุสคีมาสำหรับองค์ประกอบพิเศษแต่ละรายการ:
$ schema = Expect:: structure ([
' key ' => Expect:: string (),
])-> otherItems (Expect:: int ());
$ processor -> process ( $ schema , [ ' additional ' => 1 ]); // OK
$ processor -> process ( $ schema , [ ' additional ' => true ]); // ERROR
คุณสามารถเลิกใช้คุณสมบัติได้โดยใช้เมธอด deprecated([string $message])
ประกาศการเลิกใช้งานจะถูกส่งกลับโดย $processor->getWarnings()
:
$ schema = Expect:: structure ([
' old ' => Expect:: int ()-> deprecated ( ' The item %path% is deprecated ' ),
]);
$ processor -> process ( $ schema , [ ' old ' => 1 ]); // OK
$ processor -> getWarnings (); // ["The item 'old' is deprecated"]
ใช้ min()
และ max()
เพื่อจำกัดจำนวนองค์ประกอบสำหรับอาร์เรย์:
// array, at least 10 items, maximum 20 items
Expect:: array ()-> min ( 10 )-> max ( 20 );
สำหรับสตริง ให้จำกัดความยาว:
// string, at least 10 characters long, maximum 20 characters
Expect:: string ()-> min ( 10 )-> max ( 20 );
สำหรับตัวเลข ให้จำกัดค่า:
// integer, between 10 and 20 inclusive
Expect:: int ()-> min ( 10 )-> max ( 20 );
แน่นอนว่าเป็นไปได้ที่จะพูดถึงเฉพาะ min()
หรือเฉพาะ max()
:
// string, maximum 20 characters
Expect:: string ()-> max ( 20 );
การใช้ pattern()
คุณสามารถระบุนิพจน์ทั่วไปซึ่งสตริงอินพุต ทั้งหมด จะต้องตรงกัน (เช่น ราวกับว่ามันถูกล้อมด้วยอักขระ ^
a $
):
// just 9 digits
Expect:: string ()-> pattern ( ' d{9} ' );
คุณสามารถเพิ่มข้อ จำกัด อื่น ๆ โดยใช้ assert(callable $fn)
$ countIsEven = fn ( $ v ) => count ( $ v ) % 2 === 0 ;
$ schema = Expect:: arrayOf ( ' string ' )
-> assert ( $ countIsEven ); // the count must be even
$ processor -> process ( $ schema , [ ' a ' , ' b ' ]); // OK
$ processor -> process ( $ schema , [ ' a ' , ' b ' , ' c ' ]); // ERROR: 3 is not even
หรือ
Expect:: string ()-> assert ( ' is_file ' ); // the file must exist
คุณสามารถเพิ่มคำอธิบายของคุณเองสำหรับการยืนยันแต่ละรายการได้ มันจะเป็นส่วนหนึ่งของข้อความแสดงข้อผิดพลาด
$ schema = Expect:: arrayOf ( ' string ' )
-> assert ( $ countIsEven , ' Even items in array ' );
$ processor -> process ( $ schema , [ ' a ' , ' b ' , ' c ' ]);
// Failed assertion "Even items in array" for item with value array.
สามารถเรียกเมธอดซ้ำๆ เพื่อเพิ่มข้อจำกัดหลายรายการได้ สามารถผสมกับการเรียก transform()
และ castTo()
ได้
ข้อมูลที่ได้รับการตรวจสอบเรียบร้อยแล้วสามารถแก้ไขได้โดยใช้ฟังก์ชันที่กำหนดเอง:
// conversion to uppercase:
Expect:: string ()-> transform ( fn ( string $ s ) => strtoupper ( $ s ));
สามารถเรียกเมธอดนี้ซ้ำๆ เพื่อเพิ่มการแปลงหลายรายการได้ สามารถผสมกับการเรียก assert()
และ castTo()
ได้ การดำเนินการจะดำเนินการตามลำดับที่มีการประกาศ:
Expect:: type ( ' string|int ' )
-> castTo ( ' string ' )
-> assert ( ' ctype_lower ' , ' All characters must be lowercased ' )
-> transform ( fn ( string $ s ) => strtoupper ( $ s )); // conversion to uppercase
วิธี transform()
สามารถแปลงและตรวจสอบค่าได้พร้อมกัน ซึ่งมักจะง่ายกว่าและซ้ำซ้อนน้อยกว่าการผูกมัด transform()
และ assert()
เพื่อจุดประสงค์นี้ ฟังก์ชันจะได้รับวัตถุ NetteSchemaContext พร้อมด้วยเมธอด addError()
ซึ่งสามารถใช้เพื่อเพิ่มข้อมูลเกี่ยวกับปัญหาการตรวจสอบได้:
Expect:: string ()
-> transform ( function ( string $ s , Nette Schema Context $ context ) {
if (! ctype_lower ( $ s )) {
$ context -> addError ( ' All characters must be lowercased ' , ' my.case.error ' );
return null ;
}
return strtoupper ( $ s );
});
ข้อมูลที่ได้รับการตรวจสอบเรียบร้อยแล้วสามารถส่งได้:
Expect:: scalar ()-> castTo ( ' string ' );
นอกจากประเภท PHP ดั้งเดิมแล้ว คุณยังสามารถส่งไปยังคลาสได้อีกด้วย มันแยกความแตกต่างว่าเป็นคลาสธรรมดาที่ไม่มีคอนสตรัคเตอร์หรือคลาสที่มีคอนสตรัคเตอร์ หากคลาสไม่มี Constructor จะมีการสร้างอินสแตนซ์ขึ้นมาและองค์ประกอบทั้งหมดของโครงสร้างจะถูกเขียนไปยังคุณสมบัติ:
class Info
{
public bool $ processRefund ;
public int $ refundAmount ;
}
Expect:: structure ([
' processRefund ' => Expect:: bool (),
' refundAmount ' => Expect:: int (),
])-> castTo (Info::class);
// creates '$obj = new Info' and writes to $obj->processRefund and $obj->refundAmount
หากคลาสมีคอนสตรัคเตอร์ องค์ประกอบของโครงสร้างจะถูกส่งผ่านเป็นพารามิเตอร์ที่มีชื่อไปยังคอนสตรัคเตอร์:
class Info
{
public function __construct (
public bool $ processRefund ,
public int $ refundAmount ,
) {
}
}
// creates $obj = new Info(processRefund: ..., refundAmount: ...)
การหล่อรวมกับพารามิเตอร์สเกลาร์จะสร้างวัตถุและส่งผ่านค่าเป็นพารามิเตอร์เดียวไปยังตัวสร้าง:
Expect:: string ()-> castTo (DateTime::class);
// creates new DateTime(...)
ก่อนที่จะมีการตรวจสอบความถูกต้อง ข้อมูลสามารถทำให้เป็นมาตรฐานได้โดยใช้วิธีการ before()
ตัวอย่างเช่น ให้มีองค์ประกอบที่ต้องเป็นอาร์เรย์ของสตริง (เช่น ['a', 'b', 'c']
) แต่รับอินพุตในรูปแบบของสตริง abc
:
$ explode = fn ( $ v ) => explode ( ' ' , $ v );
$ schema = Expect:: arrayOf ( ' string ' )
-> before ( $ explode );
$ normalized = $ processor -> process ( $ schema , ' a b c ' );
// OK, returns ['a', 'b', 'c']
คุณสามารถสร้างโครงสร้างสคีมาจากคลาสได้ ตัวอย่าง:
class Config
{
public string $ name ;
public ? string $ password ;
public bool $ admin = false ;
}
$ schema = Expect:: from ( new Config );
$ data = [
' name ' => ' jeff ' ,
];
$ normalized = $ processor -> process ( $ schema , $ data );
// $normalized instanceof Config
// $normalized = {'name' => 'jeff', 'password' => null, 'admin' => false}
รองรับคลาสที่ไม่ระบุชื่อด้วย:
$ schema = Expect:: from ( new class {
public string $ name ;
public ? string $ password ;
public bool $ admin = false ;
});
เนื่องจากข้อมูลที่ได้รับจากคำจำกัดความของคลาสอาจไม่เพียงพอ คุณจึงเพิ่มสคีมาที่กำหนดเองสำหรับองค์ประกอบด้วยพารามิเตอร์ตัวที่สองได้:
$ schema = Expect:: from ( new Config , [
' name ' => Expect:: string ()-> pattern ( ' w:.* ' ),
]);