美文网首页
PHP 内核源码 Array 初入二: 强大的 array_mu

PHP 内核源码 Array 初入二: 强大的 array_mu

作者: 过往云技 | 来源:发表于2019-01-12 02:35 被阅读0次

    array_multisort 多维数组排序实现

    /* {{{ proto bool array_multisort(array &$array1 [, mixed $array1_sort_order [, mixed $array1_sort_flags [, mixed ... ]]]
       Sort multiple arrays at once similar to how ORDER BY clause works in SQL */
    PHP_FUNCTION(array_multisort)
    {
        zval*           args;
        zval**          arrays;
        Bucket**        indirect;
        uint32_t            idx;
        Bucket*         p;
        HashTable*      hash;
        int             argc;
        int             array_size;
        int             num_arrays = 0;
        int             parse_state[MULTISORT_LAST];   /* 0 - flag not allowed 1 - flag allowed */
        int             sort_order = PHP_SORT_ASC;
        int             sort_type  = PHP_SORT_REGULAR;
        int             i, k, n;
        compare_func_t  *func;
    
        ZEND_PARSE_PARAMETERS_START(1, -1)
            Z_PARAM_VARIADIC('+', args, argc)
        ZEND_PARSE_PARAMETERS_END();
    
        /* Allocate space for storing pointers to input arrays and sort flags. */
        arrays = (zval **)ecalloc(argc, sizeof(zval *));
        for (i = 0; i < MULTISORT_LAST; i++) {
            parse_state[i] = 0;
        }
        func = ARRAYG(multisort_func) = (compare_func_t*)ecalloc(argc, sizeof(compare_func_t));
    
        /* Here we go through the input arguments and parse them. Each one can
         * be either an array or a sort flag which follows an array. If not
         * specified, the sort flags defaults to PHP_SORT_ASC and PHP_SORT_REGULAR
         * accordingly. There can't be two sort flags of the same type after an
         * array, and the very first argument has to be an array. */
        for (i = 0; i < argc; i++) {
            zval *arg = &args[i];
    
            ZVAL_DEREF(arg);
            if (Z_TYPE_P(arg) == IS_ARRAY) {
                SEPARATE_ARRAY(arg);
                /* We see the next array, so we update the sort flags of
                 * the previous array and reset the sort flags. */
                if (i > 0) {
                    ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func(sort_type, sort_order != PHP_SORT_ASC);
                    sort_order = PHP_SORT_ASC;
                    sort_type = PHP_SORT_REGULAR;
                }
                arrays[num_arrays++] = arg;
    
                /* Next one may be an array or a list of sort flags. */
                for (k = 0; k < MULTISORT_LAST; k++) {
                    parse_state[k] = 1;
                }
            } else if (Z_TYPE_P(arg) == IS_LONG) {
                switch (Z_LVAL_P(arg) & ~PHP_SORT_FLAG_CASE) {
                    case PHP_SORT_ASC:
                    case PHP_SORT_DESC:
                        /* flag allowed here */
                        if (parse_state[MULTISORT_ORDER] == 1) {
                            /* Save the flag and make sure then next arg is not the current flag. */
                            sort_order = Z_LVAL_P(arg) == PHP_SORT_DESC ? PHP_SORT_DESC : PHP_SORT_ASC;
                            parse_state[MULTISORT_ORDER] = 0;
                        } else {
                            php_error_docref(NULL, E_WARNING, "Argument #%d is expected to be an array or sorting flag that has not already been specified", i + 1);
                            MULTISORT_ABORT;
                        }
                        break;
    
                    case PHP_SORT_REGULAR:
                    case PHP_SORT_NUMERIC:
                    case PHP_SORT_STRING:
                    case PHP_SORT_NATURAL:
    #if HAVE_STRCOLL
                    case PHP_SORT_LOCALE_STRING:
    #endif
                        /* flag allowed here */
                        if (parse_state[MULTISORT_TYPE] == 1) {
                            /* Save the flag and make sure then next arg is not the current flag. */
                            sort_type = (int)Z_LVAL_P(arg);
                            parse_state[MULTISORT_TYPE] = 0;
                        } else {
                            php_error_docref(NULL, E_WARNING, "Argument #%d is expected to be an array or sorting flag that has not already been specified", i + 1);
                            MULTISORT_ABORT;
                        }
                        break;
    
                    default:
                        php_error_docref(NULL, E_WARNING, "Argument #%d is an unknown sort flag", i + 1);
                        MULTISORT_ABORT;
                        break;
    
                }
            } else {
                php_error_docref(NULL, E_WARNING, "Argument #%d is expected to be an array or a sort flag", i + 1);
                MULTISORT_ABORT;
            }
        }
        /* Take care of the last array sort flags. */
        ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func(sort_type, sort_order != PHP_SORT_ASC);
    
        /* Make sure the arrays are of the same size. */
        array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
        for (i = 0; i < num_arrays; i++) {
            if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != (uint32_t)array_size) {
                php_error_docref(NULL, E_WARNING, "Array sizes are inconsistent");
                MULTISORT_ABORT;
            }
        }
    
        /* If all arrays are empty we don't need to do anything. */
        if (array_size < 1) {
            efree(func);
            efree(arrays);
            RETURN_TRUE;
        }
    
        /* Create the indirection array. This array is of size MxN, where
         * M is the number of entries in each input array and N is the number
         * of the input arrays + 1. The last column is NULL to indicate the end
         * of the row. */
        indirect = (Bucket **)safe_emalloc(array_size, sizeof(Bucket *), 0);
        for (i = 0; i < array_size; i++) {
            indirect[i] = (Bucket *)safe_emalloc((num_arrays + 1), sizeof(Bucket), 0);
        }
        for (i = 0; i < num_arrays; i++) {
            k = 0;
            for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++) {
                p = Z_ARRVAL_P(arrays[i])->arData + idx;
                if (Z_TYPE(p->val) == IS_UNDEF) continue;
                indirect[k][i] = *p;
                k++;
            }
        }
        for (k = 0; k < array_size; k++) {
            ZVAL_UNDEF(&indirect[k][num_arrays].val);
        }
    
        /* Do the actual sort magic - bada-bim, bada-boom. */
        zend_sort(indirect, array_size, sizeof(Bucket *), php_multisort_compare, (swap_func_t)array_bucket_p_sawp);
    
        /* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
        for (i = 0; i < num_arrays; i++) {
            int repack;
    
            hash = Z_ARRVAL_P(arrays[i]);
            hash->nNumUsed = array_size;
            hash->nInternalPointer = 0;
            repack = !(HT_FLAGS(hash) & HASH_FLAG_PACKED);
    
            for (n = 0, k = 0; k < array_size; k++) {
                hash->arData[k] = indirect[k][i];
                if (hash->arData[k].key == NULL) {
                    hash->arData[k].h = n++;
                } else {
                    repack = 0;
                }
            }
            hash->nNextFreeElement = array_size;
            if (repack) {
                zend_hash_to_packed(hash);
            } else if (!(HT_FLAGS(hash) & HASH_FLAG_PACKED)) {
                zend_hash_rehash(hash);
            }
        }
    
        /* Clean up. */
        for (i = 0; i < array_size; i++) {
            efree(indirect[i]);
        }
        efree(indirect);
        efree(func);
        efree(arrays);
        RETURN_TRUE;
    }
    /* }}} */
    

    PHP 利用 array_multisort

        /**
         * $array = [['test' => 'xxx'], ['test' => 'bbb']]
         * multisort($array, 'test')
         * @param array $array
         * @param $sortKey
         * @param int $sort
         * @return array|bool
         */
        function multisort(array $array, $sortKey,int $sort=SORT_ASC):?array
        {
            $tempArray = [];
            foreach($array as $value){
                if(is_array($value)){
                    $tempArray[] = $value[$sortKey];
                }
            }
            if(empty($tempArray)){
                return null;
            }
    
            array_multisort($tempArray, $sort, $array);
            return $array;
        }
    

    相关文章

      网友评论

          本文标题:PHP 内核源码 Array 初入二: 强大的 array_mu

          本文链接:https://www.haomeiwen.com/subject/rrutdqtx.html