@@ -493,7 +493,7 @@ impl<'a> Args<'a> {
493493
494494 // If the variable is `&[&Zval]` treat it as the variadic argument.
495495 let default = defaults. remove ( ident) ;
496- let nullable = type_is_nullable ( ty. as_ref ( ) , default . is_some ( ) ) ?;
496+ let nullable = type_is_nullable ( ty. as_ref ( ) ) ?;
497497 let ( variadic, as_ref, ty) = Self :: parse_typed ( ty) ;
498498 result. typed . push ( TypedArg {
499499 name : ident,
@@ -568,18 +568,20 @@ impl<'a> Args<'a> {
568568 /// # Parameters
569569 ///
570570 /// * `optional` - The first optional argument. If [`None`], the optional
571- /// arguments will be from the first nullable argument after the last
572- /// non-nullable argument to the end of the arguments.
571+ /// arguments will be from the first optional argument (nullable or has
572+ /// default) after the last required argument to the end of the arguments.
573573 pub fn split_args ( & self , optional : Option < & Ident > ) -> ( & [ TypedArg < ' a > ] , & [ TypedArg < ' a > ] ) {
574574 let mut mid = None ;
575575 for ( i, arg) in self . typed . iter ( ) . enumerate ( ) {
576+ // An argument is optional if it's nullable (Option<T>) or has a default value.
577+ let is_optional = arg. nullable || arg. default . is_some ( ) ;
576578 if let Some ( optional) = optional {
577579 if optional == arg. name {
578580 mid. replace ( i) ;
579581 }
580- } else if mid. is_none ( ) && arg . nullable {
582+ } else if mid. is_none ( ) && is_optional {
581583 mid. replace ( i) ;
582- } else if !arg . nullable {
584+ } else if !is_optional {
583585 mid. take ( ) ;
584586 }
585587 }
@@ -690,21 +692,22 @@ impl TypedArg<'_> {
690692 }
691693}
692694
693- /// Returns true of the given type is nullable in PHP.
695+ /// Returns true if the given type is nullable in PHP (i.e., it's an `Option<T>`).
696+ ///
697+ /// Note: Having a default value does NOT make a type nullable. A parameter with
698+ /// a default value is optional (can be omitted), but passing `null` explicitly
699+ /// should still be rejected unless the type is `Option<T>`.
694700// TODO(david): Eventually move to compile-time constants for this (similar to
695701// FromZval::NULLABLE).
696- pub fn type_is_nullable ( ty : & Type , has_default : bool ) -> Result < bool > {
702+ pub fn type_is_nullable ( ty : & Type ) -> Result < bool > {
697703 Ok ( match ty {
698- syn:: Type :: Path ( path) => {
699- has_default
700- || path
701- . path
702- . segments
703- . iter ( )
704- . next_back ( )
705- . is_some_and ( |seg| seg. ident == "Option" )
706- }
707- syn:: Type :: Reference ( _) => false , /* Reference cannot be nullable unless */
704+ Type :: Path ( path) => path
705+ . path
706+ . segments
707+ . iter ( )
708+ . next_back ( )
709+ . is_some_and ( |seg| seg. ident == "Option" ) ,
710+ Type :: Reference ( _) => false , /* Reference cannot be nullable unless */
708711 // wrapped in `Option` (in that case it'd be a Path).
709712 _ => bail ! ( ty => "Unsupported argument type." ) ,
710713 } )
0 commit comments