Hello,

While browsing through the changelog for PHP 5.4, I noticed the new short syntax for creating arrays. I had been wanting this feature for a very long time, and this pushed me to make a proposal for a similar syntax change to make PHP more elegant.

My proposal is simply to add an infix operator version of in_array. One of the main goals is to avoid statements such as:

if(($v1=="a1" || $v=="a2" || ...) && ($v2=="b1" || $v2=="b2" ||...) && ...)

by replacing them with:

if($v1 in ["a1","a2",...] && $v2 in ["b1","b2",...])

To back up my proposal I have made a patch that implements this operator. It is quite straightforward for the most part, but there are a few design decisions to validate, including the behavior when the operator is used on a non-array (I suppose it might be possible to extend it to objects but the usefulness of this seems questionable), and whether to use strict or loose equality by default (I chose the later).

There are actually two versions of the patch, one that will change the internal pointer of the array to either the first matching element or the last if none matches, the other leaving the internal pointer unchanged. Although I prefer the first solution because it can emulate array_search using key() without resorting to ===FALSE, I reckon this kind of behavior is depreciated since PHP 4, hence the "pointersafe" version. If you want to try this patch remember to regenerate your
zend_vm_execute.h and zend_vm_opcodes.h by running zend_vm_gen.php.

Although I am well aware that the core language should never be changed thoughtlessly and that PHP generally dislikes most kinds of syntactic sugar (it's not Perl, alright), I hope you will still consider this proposal for a future version of PHP.

With Regards,

Antoine Delignat-Lavaud
Index: zend_vm_opcodes.h
===================================================================
--- zend_vm_opcodes.h   (revision 319106)
+++ zend_vm_opcodes.h   (working copy)
@@ -159,3 +159,4 @@
 #define ZEND_SEPARATE                        156
 #define ZEND_QM_ASSIGN_VAR                   157
 #define ZEND_JMP_SET_VAR                     158
+#define ZEND_IS_IN                           159
Index: zend_language_parser.y
===================================================================
--- zend_language_parser.y      (revision 319106)
+++ zend_language_parser.y      (working copy)
@@ -96,6 +96,8 @@
 %nonassoc '<' T_IS_SMALLER_OR_EQUAL '>' T_IS_GREATER_OR_EQUAL
 %token T_IS_SMALLER_OR_EQUAL "<= (T_IS_SMALLER_OR_EQUAL)"
 %token T_IS_GREATER_OR_EQUAL ">= (T_IS_GREATER_OR_EQUAL)"
+%nonassoc T_IS_IN
+%token T_IS_IN "in (T_IS_IN)"
 %left T_SL T_SR
 %token T_SL "<< (T_SL)"
 %token T_SR ">> (T_SR)"
@@ -769,6 +771,7 @@
        |       expr T_IS_SMALLER_OR_EQUAL expr { 
zend_do_binary_op(ZEND_IS_SMALLER_OR_EQUAL, &$$, &$1, &$3 TSRMLS_CC); }
        |       expr '>' expr                                   { 
zend_do_binary_op(ZEND_IS_SMALLER, &$$, &$3, &$1 TSRMLS_CC); }
        |       expr T_IS_GREATER_OR_EQUAL expr { 
zend_do_binary_op(ZEND_IS_SMALLER_OR_EQUAL, &$$, &$3, &$1 TSRMLS_CC); }
+       |       expr T_IS_IN expr                               { 
zend_do_binary_op(ZEND_IS_IN, &$$, &$3, &$1 TSRMLS_CC); }
        |       expr T_INSTANCEOF class_name_reference { 
zend_do_instanceof(&$$, &$1, &$3, 0 TSRMLS_CC); }
        |       '(' expr ')'    { $$ = $2; }
        |       new_expr                { $$ = $1; }
Index: zend_vm_def.h
===================================================================
--- zend_vm_def.h       (revision 319106)
+++ zend_vm_def.h       (working copy)
@@ -241,6 +241,21 @@
        ZEND_VM_NEXT_OPCODE();
 }
 
+ZEND_VM_HANDLER(159, ZEND_IS_IN, CONST|TMP|VAR|CV, CONST|TMP|VAR|CV)
+{
+       USE_OPLINE
+       zend_free_op free_op1, free_op2;
+
+       SAVE_OPLINE();
+       is_in_function(&EX_T(opline->result.var).tmp_var,
+               GET_OP1_ZVAL_PTR(BP_VAR_R),
+               GET_OP2_ZVAL_PTR(BP_VAR_R) TSRMLS_CC);
+       FREE_OP1();
+       FREE_OP2();
+       CHECK_EXCEPTION();
+       ZEND_VM_NEXT_OPCODE();
+}
+
 ZEND_VM_HANDLER(9, ZEND_BW_OR, CONST|TMP|VAR|CV, CONST|TMP|VAR|CV)
 {
        USE_OPLINE
Index: zend_language_scanner.l
===================================================================
--- zend_language_scanner.l     (revision 319106)
+++ zend_language_scanner.l     (working copy)
@@ -1350,6 +1350,10 @@
        return T_IS_GREATER_OR_EQUAL;
 }
 
+<ST_IN_SCRIPTING>"in" {
+       return T_IS_IN;
+}
+
 <ST_IN_SCRIPTING>"+=" {
        return T_PLUS_EQUAL;
 }
Index: zend_operators.c
===================================================================
--- zend_operators.c    (revision 319106)
+++ zend_operators.c    (working copy)
@@ -1672,6 +1672,33 @@
 }
 /* }}} */
 
+ZEND_API int is_in_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* 
{{{ */
+{
+       switch (Z_TYPE_P(op1)) {
+               case IS_ARRAY: {
+                               zval **val;
+
+                               
zend_hash_internal_pointer_reset(Z_ARRVAL_P(op1));
+                               while 
(zend_hash_get_current_data(Z_ARRVAL_P(op1), (void **)&val) == SUCCESS) {
+                                               if (is_equal_function(result, 
op2, *val TSRMLS_CC) == FAILURE) {
+                                                       return FAILURE;
+                                               }
+                                               if (Z_BVAL_P(result)) {
+                                                       return SUCCESS;
+                                               }
+                                               
zend_hash_move_forward(Z_ARRVAL_P(op1));
+                               }
+                       }
+                       ZVAL_BOOL(result, 0);
+                       return SUCCESS;
+               default:
+                       zend_error(E_WARNING, "Trying to search for an element 
in a non-array");
+                       ZVAL_BOOL(result, 0);
+                       return SUCCESS;
+       }
+}
+/* }}} */
+
 ZEND_API zend_bool instanceof_function_ex(const zend_class_entry *instance_ce, 
const zend_class_entry *ce, zend_bool interfaces_only TSRMLS_DC) /* {{{ */
 {
        zend_uint i;
Index: zend_operators.h
===================================================================
--- zend_operators.h    (revision 319106)
+++ zend_operators.h    (working copy)
@@ -61,6 +61,7 @@
 ZEND_API int is_not_equal_function(zval *result, zval *op1, zval *op2 
TSRMLS_DC);
 ZEND_API int is_smaller_function(zval *result, zval *op1, zval *op2 TSRMLS_DC);
 ZEND_API int is_smaller_or_equal_function(zval *result, zval *op1, zval *op2 
TSRMLS_DC);
+ZEND_API int is_in_function(zval *result, zval *op1, zval *op2 TSRMLS_DC);
 
 ZEND_API zend_bool instanceof_function_ex(const zend_class_entry *instance_ce, 
const zend_class_entry *ce, zend_bool interfaces_only TSRMLS_DC);
 ZEND_API zend_bool instanceof_function(const zend_class_entry *instance_ce, 
const zend_class_entry *ce TSRMLS_DC);
Index: zend_opcode.c
===================================================================
--- zend_opcode.c       (revision 319106)
+++ zend_opcode.c       (working copy)
@@ -617,6 +617,9 @@
                case ZEND_IS_SMALLER_OR_EQUAL:
                        return (binary_op_type) is_smaller_or_equal_function;
                        break;
+               case ZEND_IS_IN:
+                       return (binary_op_type) is_in_function;
+                       break;
                case ZEND_BW_OR:
                case ZEND_ASSIGN_BW_OR:
                        return (binary_op_type) bitwise_or_function;
Index: zend_vm_opcodes.h
===================================================================
--- zend_vm_opcodes.h   (revision 319106)
+++ zend_vm_opcodes.h   (working copy)
@@ -159,3 +159,4 @@
 #define ZEND_SEPARATE                        156
 #define ZEND_QM_ASSIGN_VAR                   157
 #define ZEND_JMP_SET_VAR                     158
+#define ZEND_IS_IN                           159
Index: zend_language_parser.y
===================================================================
--- zend_language_parser.y      (revision 319106)
+++ zend_language_parser.y      (working copy)
@@ -96,6 +96,8 @@
 %nonassoc '<' T_IS_SMALLER_OR_EQUAL '>' T_IS_GREATER_OR_EQUAL
 %token T_IS_SMALLER_OR_EQUAL "<= (T_IS_SMALLER_OR_EQUAL)"
 %token T_IS_GREATER_OR_EQUAL ">= (T_IS_GREATER_OR_EQUAL)"
+%nonassoc T_IS_IN
+%token T_IS_IN "in (T_IS_IN)"
 %left T_SL T_SR
 %token T_SL "<< (T_SL)"
 %token T_SR ">> (T_SR)"
@@ -769,6 +771,7 @@
        |       expr T_IS_SMALLER_OR_EQUAL expr { 
zend_do_binary_op(ZEND_IS_SMALLER_OR_EQUAL, &$$, &$1, &$3 TSRMLS_CC); }
        |       expr '>' expr                                   { 
zend_do_binary_op(ZEND_IS_SMALLER, &$$, &$3, &$1 TSRMLS_CC); }
        |       expr T_IS_GREATER_OR_EQUAL expr { 
zend_do_binary_op(ZEND_IS_SMALLER_OR_EQUAL, &$$, &$3, &$1 TSRMLS_CC); }
+       |       expr T_IS_IN expr                               { 
zend_do_binary_op(ZEND_IS_IN, &$$, &$3, &$1 TSRMLS_CC); }
        |       expr T_INSTANCEOF class_name_reference { 
zend_do_instanceof(&$$, &$1, &$3, 0 TSRMLS_CC); }
        |       '(' expr ')'    { $$ = $2; }
        |       new_expr                { $$ = $1; }
Index: zend_vm_def.h
===================================================================
--- zend_vm_def.h       (revision 319106)
+++ zend_vm_def.h       (working copy)
@@ -241,6 +241,21 @@
        ZEND_VM_NEXT_OPCODE();
 }
 
+ZEND_VM_HANDLER(159, ZEND_IS_IN, CONST|TMP|VAR|CV, CONST|TMP|VAR|CV)
+{
+       USE_OPLINE
+       zend_free_op free_op1, free_op2;
+
+       SAVE_OPLINE();
+       is_in_function(&EX_T(opline->result.var).tmp_var,
+               GET_OP1_ZVAL_PTR(BP_VAR_R),
+               GET_OP2_ZVAL_PTR(BP_VAR_R) TSRMLS_CC);
+       FREE_OP1();
+       FREE_OP2();
+       CHECK_EXCEPTION();
+       ZEND_VM_NEXT_OPCODE();
+}
+
 ZEND_VM_HANDLER(9, ZEND_BW_OR, CONST|TMP|VAR|CV, CONST|TMP|VAR|CV)
 {
        USE_OPLINE
Index: zend_language_scanner.l
===================================================================
--- zend_language_scanner.l     (revision 319106)
+++ zend_language_scanner.l     (working copy)
@@ -1350,6 +1350,10 @@
        return T_IS_GREATER_OR_EQUAL;
 }
 
+<ST_IN_SCRIPTING>"in" {
+       return T_IS_IN;
+}
+
 <ST_IN_SCRIPTING>"+=" {
        return T_PLUS_EQUAL;
 }
Index: zend_operators.c
===================================================================
--- zend_operators.c    (revision 319106)
+++ zend_operators.c    (working copy)
@@ -1672,6 +1672,37 @@
 }
 /* }}} */
 
+ZEND_API int is_in_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* 
{{{ */
+{
+       switch (Z_TYPE_P(op1)) {
+               case IS_ARRAY: {
+                               zval **val;
+                               HashPointer pos;
+
+                               zend_hash_get_pointer(Z_ARRVAL_P(op1), &pos);
+                               
zend_hash_internal_pointer_reset(Z_ARRVAL_P(op1));
+                               while 
(zend_hash_get_current_data(Z_ARRVAL_P(op1), (void **)&val) == SUCCESS) {
+                                               if (is_equal_function(result, 
op2, *val TSRMLS_CC) == FAILURE) {
+                                                       return FAILURE;
+                                               }
+                                               if (Z_BVAL_P(result)) {
+                                                       
zend_hash_set_pointer(Z_ARRVAL_P(op1), &pos);
+                                                       return SUCCESS;
+                                               }
+                                               
zend_hash_move_forward(Z_ARRVAL_P(op1));
+                               }
+                               zend_hash_set_pointer(Z_ARRVAL_P(op1), &pos);
+                       }
+                       ZVAL_BOOL(result, 0);
+                       return SUCCESS;
+               default:
+                       zend_error(E_WARNING, "Trying to search for an element 
in a non-array");
+                       ZVAL_BOOL(result, 0);
+                       return SUCCESS;
+       }
+}
+/* }}} */
+
 ZEND_API zend_bool instanceof_function_ex(const zend_class_entry *instance_ce, 
const zend_class_entry *ce, zend_bool interfaces_only TSRMLS_DC) /* {{{ */
 {
        zend_uint i;
Index: zend_operators.h
===================================================================
--- zend_operators.h    (revision 319106)
+++ zend_operators.h    (working copy)
@@ -61,6 +61,7 @@
 ZEND_API int is_not_equal_function(zval *result, zval *op1, zval *op2 
TSRMLS_DC);
 ZEND_API int is_smaller_function(zval *result, zval *op1, zval *op2 TSRMLS_DC);
 ZEND_API int is_smaller_or_equal_function(zval *result, zval *op1, zval *op2 
TSRMLS_DC);
+ZEND_API int is_in_function(zval *result, zval *op1, zval *op2 TSRMLS_DC);
 
 ZEND_API zend_bool instanceof_function_ex(const zend_class_entry *instance_ce, 
const zend_class_entry *ce, zend_bool interfaces_only TSRMLS_DC);
 ZEND_API zend_bool instanceof_function(const zend_class_entry *instance_ce, 
const zend_class_entry *ce TSRMLS_DC);
Index: zend_opcode.c
===================================================================
--- zend_opcode.c       (revision 319106)
+++ zend_opcode.c       (working copy)
@@ -617,6 +617,9 @@
                case ZEND_IS_SMALLER_OR_EQUAL:
                        return (binary_op_type) is_smaller_or_equal_function;
                        break;
+               case ZEND_IS_IN:
+                       return (binary_op_type) is_in_function;
+                       break;
                case ZEND_BW_OR:
                case ZEND_ASSIGN_BW_OR:
                        return (binary_op_type) bitwise_or_function;

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to