4040
4141use CodeIgniter \Database \Exceptions \DatabaseException ;
4242use CodeIgniter \Database \Exceptions \DataException ;
43+ use Closure ;
4344
4445/**
4546 * Class BaseBuilder
@@ -701,10 +702,19 @@ protected function whereHaving(string $qb_key, $key, $value = null, string $type
701702 }
702703 else
703704 {
704- $ k .= $ op ;
705+ $ k .= " $ op" ;
705706 }
706707
707- $ v = " : $ bind: " ;
708+ if ($ v instanceof Closure)
709+ {
710+ $ builder = clone $ this ;
711+ $ builder ->from ([], true )->resetQuery ();
712+ $ v = '( ' . str_replace ("\n" , ' ' , $ v ($ builder )->getCompiledSelect ()) . ') ' ;
713+ }
714+ else
715+ {
716+ $ v = " : $ bind: " ;
717+ }
708718 }
709719 elseif (! $ this ->hasOperator ($ k ) && $ qb_key !== 'QBHaving ' )
710720 {
@@ -733,13 +743,13 @@ protected function whereHaving(string $qb_key, $key, $value = null, string $type
733743 * Generates a WHERE field IN('item', 'item') SQL query,
734744 * joined with 'AND' if appropriate.
735745 *
736- * @param string $key The field to search
737- * @param array $values The values searched on
738- * @param boolean $escape
746+ * @param string $key The field to search
747+ * @param array|Closure $values The values searched on, or anonymous function with subquery
748+ * @param boolean $escape
739749 *
740750 * @return BaseBuilder
741751 */
742- public function whereIn (string $ key = null , array $ values = null , bool $ escape = null )
752+ public function whereIn (string $ key = null , $ values = null , bool $ escape = null )
743753 {
744754 return $ this ->_whereIn ($ key , $ values , false , 'AND ' , $ escape );
745755 }
@@ -752,13 +762,13 @@ public function whereIn(string $key = null, array $values = null, bool $escape =
752762 * Generates a WHERE field IN('item', 'item') SQL query,
753763 * joined with 'OR' if appropriate.
754764 *
755- * @param string $key The field to search
756- * @param array $values The values searched on
757- * @param boolean $escape
765+ * @param string $key The field to search
766+ * @param array|Closure $values The values searched on, or anonymous function with subquery
767+ * @param boolean $escape
758768 *
759769 * @return BaseBuilder
760770 */
761- public function orWhereIn (string $ key = null , array $ values = null , bool $ escape = null )
771+ public function orWhereIn (string $ key = null , $ values = null , bool $ escape = null )
762772 {
763773 return $ this ->_whereIn ($ key , $ values , false , 'OR ' , $ escape );
764774 }
@@ -771,13 +781,13 @@ public function orWhereIn(string $key = null, array $values = null, bool $escape
771781 * Generates a WHERE field NOT IN('item', 'item') SQL query,
772782 * joined with 'AND' if appropriate.
773783 *
774- * @param string $key The field to search
775- * @param array $values The values searched on
776- * @param boolean $escape
784+ * @param string $key The field to search
785+ * @param array|Closure $values The values searched on, or anonymous function with subquery
786+ * @param boolean $escape
777787 *
778788 * @return BaseBuilder
779789 */
780- public function whereNotIn (string $ key = null , array $ values = null , bool $ escape = null )
790+ public function whereNotIn (string $ key = null , $ values = null , bool $ escape = null )
781791 {
782792 return $ this ->_whereIn ($ key , $ values , true , 'AND ' , $ escape );
783793 }
@@ -790,13 +800,13 @@ public function whereNotIn(string $key = null, array $values = null, bool $escap
790800 * Generates a WHERE field NOT IN('item', 'item') SQL query,
791801 * joined with 'OR' if appropriate.
792802 *
793- * @param string $key The field to search
794- * @param array $values The values searched on
795- * @param boolean $escape
803+ * @param string $key The field to search
804+ * @param array|Closure $values The values searched on, or anonymous function with subquery
805+ * @param boolean $escape
796806 *
797807 * @return BaseBuilder
798808 */
799- public function orWhereNotIn (string $ key = null , array $ values = null , bool $ escape = null )
809+ public function orWhereNotIn (string $ key = null , $ values = null , bool $ escape = null )
800810 {
801811 return $ this ->_whereIn ($ key , $ values , true , 'OR ' , $ escape );
802812 }
@@ -811,17 +821,17 @@ public function orWhereNotIn(string $key = null, array $values = null, bool $esc
811821 * @used-by whereNotIn()
812822 * @used-by orWhereNotIn()
813823 *
814- * @param string $key The field to search
815- * @param array $values The values searched on
816- * @param boolean $not If the statement would be IN or NOT IN
817- * @param string $type
818- * @param boolean $escape
824+ * @param string $key The field to search
825+ * @param array|Closure $values The values searched on, or anonymous function with subquery
826+ * @param boolean $not If the statement would be IN or NOT IN
827+ * @param string $type
828+ * @param boolean $escape
819829 *
820830 * @return BaseBuilder
821831 */
822- protected function _whereIn (string $ key = null , array $ values = null , bool $ not = false , string $ type = 'AND ' , bool $ escape = null )
832+ protected function _whereIn (string $ key = null , $ values = null , bool $ not = false , string $ type = 'AND ' , bool $ escape = null )
823833 {
824- if ($ key === null || $ values === null )
834+ if ($ key === null || $ values === null || (! is_array ( $ values ) && ! ( $ values instanceof Closure)) )
825835 {
826836 return $ this ;
827837 }
@@ -837,13 +847,21 @@ protected function _whereIn(string $key = null, array $values = null, bool $not
837847
838848 $ not = ($ not ) ? ' NOT ' : '' ;
839849
840- $ where_in = array_values ($ values );
841- $ ok = $ this ->setBind ($ ok , $ where_in , $ escape );
850+ if ($ values instanceof Closure)
851+ {
852+ $ builder = clone $ this ;
853+ $ builder ->from ([], true )->resetQuery ();
854+ $ ok = str_replace ("\n" , ' ' , $ values ($ builder )->getCompiledSelect ());
855+ }
856+ else
857+ {
858+ $ ok = $ this ->setBind ($ ok , array_values ($ values ), $ escape );
859+ }
842860
843861 $ prefix = empty ($ this ->QBWhere ) ? $ this ->groupGetType ('' ) : $ this ->groupGetType ($ type );
844862
845863 $ where_in = [
846- 'condition ' => $ prefix . $ key . $ not . " IN : {$ ok }: " ,
864+ 'condition ' => $ prefix . $ key . $ not . ( $ values instanceof Closure ? " IN ( $ ok ) " : " IN : {$ ok }: ") ,
847865 'escape ' => false ,
848866 ];
849867
@@ -2640,7 +2658,6 @@ protected function compileWhereHaving(string $qb_key): string
26402658 {
26412659 continue ;
26422660 }
2643-
26442661 // $matches = array(
26452662 // 0 => '(test <= foo)', /* the whole thing */
26462663 // 1 => '(', /* optional */
@@ -2968,7 +2985,7 @@ protected function getOperator(string $str, bool $list = false)
29682985 ];
29692986 }
29702987
2971- return preg_match_all ('/ ' . implode ('| ' , $ _operators ) . '/i ' , $ str , $ match ) ? ($ list ? $ match [0 ] : $ match [0 ][count ( $ match [ 0 ]) - 1 ]) : false ;
2988+ return preg_match_all ('/ ' . implode ('| ' , $ _operators ) . '/i ' , $ str , $ match ) ? ($ list ? $ match [0 ] : $ match [0 ][0 ]) : false ;
29722989 }
29732990
29742991 // --------------------------------------------------------------------
0 commit comments