Index: trunk/extensions/ArrayExtension/ArrayExtension.php |
— | — | @@ -64,7 +64,8 @@ |
65 | 65 | |
66 | 66 | |
67 | 67 | /** |
68 | | - * Full compatbility to versions before 1.4 |
| 68 | + * Full compatbility to versions before 1.4. |
| 69 | + * Set to true by default since version 2.0. |
69 | 70 | * |
70 | 71 | * @since 1.4 alpha |
71 | 72 | * |
— | — | @@ -120,10 +121,10 @@ |
121 | 122 | self::initFunction( $parser, 'arrayreset', SFH_OBJECT_ARGS ); |
122 | 123 | self::initFunction( $parser, 'arrayunique' ); |
123 | 124 | self::initFunction( $parser, 'arraysort' ); |
124 | | - self::initFunction( $parser, 'arraymerge' ); |
125 | | - self::initFunction( $parser, 'arrayunion' ); |
126 | | - self::initFunction( $parser, 'arraydiff' ); |
127 | | - self::initFunction( $parser, 'arrayintersect' ); |
| 125 | + self::initFunction( $parser, 'arraymerge', SFH_OBJECT_ARGS ); |
| 126 | + self::initFunction( $parser, 'arrayunion', SFH_OBJECT_ARGS ); |
| 127 | + self::initFunction( $parser, 'arraydiff', SFH_OBJECT_ARGS ); |
| 128 | + self::initFunction( $parser, 'arrayintersect', SFH_OBJECT_ARGS ); |
128 | 129 | |
129 | 130 | return true; |
130 | 131 | } |
— | — | @@ -157,7 +158,7 @@ |
158 | 159 | #################### |
159 | 160 | |
160 | 161 | /////////////////////////////////////////////////////////// |
161 | | - // PART 1. constructor |
| 162 | + // PART 1. Array Construction |
162 | 163 | /////////////////////////////////////////////////////////// |
163 | 164 | |
164 | 165 | /** |
— | — | @@ -271,7 +272,7 @@ |
272 | 273 | |
273 | 274 | |
274 | 275 | /////////////////////////////////////////////////////////// |
275 | | - // PART 2. print |
| 276 | + // PART 2. Extracting Information |
276 | 277 | /////////////////////////////////////////////////////////// |
277 | 278 | |
278 | 279 | |
— | — | @@ -543,11 +544,50 @@ |
544 | 545 | $store->setArray( $arrayId_new, $newArr ); |
545 | 546 | return ''; |
546 | 547 | } |
| 548 | + |
| 549 | + /** |
| 550 | + * extract a slice from an array |
| 551 | + * usage: |
| 552 | + * {{#arrayslice:arrayid_new|arrayid|offset|length}} |
| 553 | + * |
| 554 | + * extract a slice from an array |
| 555 | + * see: http://www.php.net/manual/en/function.array-slice.php |
| 556 | + */ |
| 557 | + static function pf_arrayslice( Parser &$parser, $arrayId_new, $arrayId = null , $offset = 0, $length = null ) { |
| 558 | + $store = self::get( $parser ); |
| 559 | + if( $arrayId === null ) { |
| 560 | + global $egArrayExtensionCompatbilityMode; |
| 561 | + if( ! $egArrayExtensionCompatbilityMode ) { // COMPATBILITY-MODE |
| 562 | + $store->setArray( $arrayId_new ); |
| 563 | + } |
| 564 | + return ''; |
| 565 | + } |
| 566 | + // get target array before overwriting it in any way |
| 567 | + $array = $store->getArray( $arrayId ); |
547 | 568 | |
| 569 | + // make sure at least an empty array exists if we return early |
| 570 | + $store->setArray( $arrayId_new ); |
| 571 | + |
| 572 | + if( $array === null |
| 573 | + || ! is_numeric( $offset ) // don't ignore invalid offset |
| 574 | + ) { |
| 575 | + return ''; |
| 576 | + } |
548 | 577 | |
549 | | - |
| 578 | + if( ! is_numeric( $length ) ) { |
| 579 | + $length = null; // ignore invalid input, slice till end |
| 580 | + } |
| 581 | + |
| 582 | + // array_slice will re-organize keys |
| 583 | + $newArray = array_slice( $array, $offset, $length ); |
| 584 | + $store->setArray( $arrayId_new, $newArray ); |
| 585 | + |
| 586 | + return ''; |
| 587 | + } |
| 588 | + |
| 589 | + |
550 | 590 | /////////////////////////////////////////////////////////// |
551 | | - // PART 3. alter an array |
| 591 | + // PART 3. Array Alteration |
552 | 592 | /////////////////////////////////////////////////////////// |
553 | 593 | |
554 | 594 | /** |
— | — | @@ -652,173 +692,150 @@ |
653 | 693 | |
654 | 694 | |
655 | 695 | /////////////////////////////////////////////////////////// |
656 | | - // PART 4. create an array |
| 696 | + // PART 4. Array Interaction |
657 | 697 | /////////////////////////////////////////////////////////// |
658 | | - |
| 698 | + |
659 | 699 | /** |
660 | | - * merge two arrays, keep duplicated values |
661 | | - * usage: |
662 | | - * {{#arraymerge:arrayid_new|arrayid1|arrayid2}} |
663 | | - * |
664 | | - * merge values two arrayes identified by arrayid1 and arrayid2 into a new array identified by arrayid_new. |
665 | | - * this merge differs from array_merge of php because it merges values. |
666 | | - */ |
667 | | - static function pf_arraymerge( Parser &$parser, $arrayId_new, $arrayId1 = null, $arrayId2 = null ) { |
668 | | - if( ! isset( $arrayId_new ) || ! isset( $arrayId1 ) || ! isset( $arrayId2 ) ) { |
669 | | - return ''; |
670 | | - } |
671 | | - $store = self::get( $parser ); |
672 | | - |
673 | | - $ret = $store->validate_array_by_arrayId( $arrayId1 ); |
674 | | - if( $ret !== true ) { |
675 | | - return ''; |
676 | | - } |
677 | | - |
678 | | - $temp_array = array(); |
679 | | - foreach( $store->mArrays[ $arrayId1 ] as $entry ) { |
680 | | - array_push ( $temp_array, $entry ); |
681 | | - } |
682 | | - |
683 | | - if( isset( $arrayId2 ) && strlen( $arrayId2 ) > 0 ) { |
684 | | - $ret = $store->validate_array_by_arrayId( $arrayId2 ); |
685 | | - if( $ret === true ) { |
686 | | - foreach( $store->mArrays[ $arrayId2 ] as $entry ) { |
687 | | - array_push ( $temp_array, $entry ); |
688 | | - } |
689 | | - } |
690 | | - } |
691 | | - |
692 | | - $store->mArrays[$arrayId_new] = $temp_array; |
693 | | - return ''; |
694 | | - } |
695 | | - |
696 | | - /** |
697 | | - * extract a slice from an array |
698 | | - * usage: |
699 | | - * {{#arrayslice:arrayid_new|arrayid|offset|length}} |
700 | | - * |
701 | | - * extract a slice from an array |
702 | | - * see: http://www.php.net/manual/en/function.array-slice.php |
703 | | - */ |
704 | | - static function pf_arrayslice( Parser &$parser, $arrayId_new, $arrayId = null , $offset = 0, $length = null ) { |
705 | | - $store = self::get( $parser ); |
706 | | - if( $arrayId === null ) { |
707 | | - global $egArrayExtensionCompatbilityMode; |
708 | | - if( ! $egArrayExtensionCompatbilityMode ) { // COMPATBILITY-MODE |
709 | | - $store->setArray( $arrayId_new ); |
710 | | - } |
711 | | - return ''; |
712 | | - } |
713 | | - // get target array before overwriting it in any way |
714 | | - $array = $store->getArray( $arrayId ); |
715 | | - |
716 | | - // make sure at least an empty array exists if we return early |
717 | | - $store->setArray( $arrayId_new ); |
718 | | - |
719 | | - if( $array === null |
720 | | - || ! is_numeric( $offset ) // don't ignore invalid offset |
721 | | - ) { |
722 | | - return ''; |
723 | | - } |
724 | | - |
725 | | - if( ! is_numeric( $length ) ) { |
726 | | - $length = null; // ignore invalid input, slice till end |
727 | | - } |
728 | | - |
729 | | - // array_slice will re-organize keys |
730 | | - $newArray = array_slice( $array, $offset, $length ); |
731 | | - $store->setArray( $arrayId_new, $newArray ); |
732 | | - |
| 700 | + * Merge values two arrayes identified by arrayid1 and arrayid2 into a new array identified by arrayid_new. |
| 701 | + * This merge differs from array_merge of php because it merges values. |
| 702 | + * |
| 703 | + * Usage: |
| 704 | + * {{#arraymerge:arrayid_new |array1 |array2 |... |array n}} |
| 705 | + * See: http://www.php.net/manual/en/function.array-merge.php |
| 706 | + */ |
| 707 | + static function pfObj_arraymerge( &$parser, $frame, $args) { |
| 708 | + self::get( $parser )->multiArrayOperation( $frame, $args, __FUNCTION__, false ); |
733 | 709 | return ''; |
734 | 710 | } |
735 | | - |
736 | | - ///////////////////////////////////////////////// / |
737 | | - // SET OPERATIONS: a set does not have duplicated element |
| 711 | + private function multi_arraymerge( $array1, $array2 ) { |
| 712 | + // keys will not be re-organized |
| 713 | + return array_merge( $array1, $array2 ); |
| 714 | + } |
738 | 715 | |
739 | | - /** |
740 | | - * set operation, {red, white} = {red, white} union {red} |
741 | | - * usage: |
742 | | - * {{#arrayunion:arrayid_new|arrayid1|arrayid2}} |
743 | | - |
744 | | - * similar to arraymerge, this union works on values. |
745 | | - */ |
746 | | - static function pf_arrayunion( Parser &$parser, $arrayId_new, $arrayId1 = null , $arrayId2 = null ) { |
747 | | - $store = self::get( $parser ); |
748 | | - if ( ! isset( $arrayId_new ) || ! isset( $arrayId1 ) || ! isset( $arrayId2 ) ) { |
749 | | - return ''; |
750 | | - } |
751 | | - if( ! isset( $arrayId1 ) || ! $store->arrayExists( $arrayId1 ) ) { |
752 | | - return ''; |
753 | | - } |
754 | | - if( ! isset( $arrayId2 ) || ! $store->arrayExists( $arrayId2 ) ) { |
755 | | - return ''; |
756 | | - } |
757 | | - |
758 | | - self::pf_arraymerge( $parser, $arrayId_new, $arrayId1, $arrayId2 ); |
759 | | - $store->setArray( $arrayId_new, array_unique ( $store->getArray( $arrayId_new ) ) ); |
760 | | - |
761 | | - return ''; |
762 | | - } |
| 716 | + /** |
| 717 | + * Usage: |
| 718 | + * {{#arrayunion:arrayid_new|arrayid1|arrayid2}} |
| 719 | + * |
| 720 | + * Set operation, {red, white} = {red, white} union {red} |
| 721 | + * Similar to arraymerge but with unique values. This union works on values. |
| 722 | + */ |
| 723 | + static function pfObj_arrayunion( &$parser, $frame, $args) { |
| 724 | + self::get( $parser )->multiArrayOperation( $frame, $args, __FUNCTION__, false ); |
| 725 | + return ''; |
| 726 | + } |
| 727 | + private function multi_arrayunion( $array1, $array2 ) { |
| 728 | + // keys will not be re-organized |
| 729 | + return array_unique( array_merge( $array1, $array2 ) ); |
| 730 | + } |
763 | 731 | |
764 | 732 | /** |
765 | | - * set operation, {red} = {red, white} intersect {red,black} |
766 | | - * usage: |
767 | | - * {{#arrayintersect:arrayid_new|arrayid1|arrayid2}} |
768 | | - * See: http://www.php.net/manual/en/function.array-intersect.php |
769 | | - */ |
770 | | - static function pf_arrayintersect( Parser &$parser, $arrayId_new, $arrayId1 = null , $arrayId2 = null ) { |
771 | | - $store = self::get( $parser ); |
772 | | - if ( ! isset( $arrayId_new ) || ! isset( $arrayId1 ) || ! isset( $arrayId2 ) ) { |
773 | | - return ''; |
774 | | - } |
775 | | - if( ! isset( $arrayId1 ) || ! $store->arrayExists( $arrayId1 ) ) { |
776 | | - return ''; |
777 | | - } |
778 | | - if( ! isset( $arrayId2 ) || ! $store->arrayExists( $arrayId2 ) ) { |
779 | | - return ''; |
780 | | - } |
| 733 | + * Usage: |
| 734 | + * {{#arrayintersect:arrayid_new |array1 |array2 |... |array n}} |
| 735 | + * |
| 736 | + * Set operation, {red} = {red, white} intersect {red,black} |
| 737 | + * See: http://www.php.net/manual/en/function.array-intersect.php |
| 738 | + */ |
| 739 | + static function pfObj_arrayintersect( &$parser, $frame, $args) { |
| 740 | + self::get( $parser )->multiArrayOperation( $frame, $args, __FUNCTION__, false ); |
| 741 | + return ''; |
| 742 | + } |
| 743 | + private function multi_arrayintersect( $array1, $array2 ) { |
| 744 | + // keys will be preserved! |
| 745 | + return array_intersect( $array1, $array2 ); |
| 746 | + } |
781 | 747 | |
782 | | - // keys will be preserved... |
783 | | - $newArray = array_intersect( array_unique( $store->mArrays[$arrayId1] ), array_unique( $store->mArrays[$arrayId2] ) ); |
784 | | - |
785 | | - // ...so we have to reorganize the key order |
786 | | - $store->mArrays[$arrayId_new] = self::sanitizeArray( $newArray ); |
787 | | - |
788 | | - return ''; |
789 | | - } |
790 | | - |
791 | 748 | /** |
792 | | - * |
793 | | - * usage: |
794 | | - * {{#arraydiff:arrayid_new|arrayid1|arrayid2}} |
795 | | - * |
796 | | - * set operation, {white} = {red, white} - {red} |
797 | | - * see: http://www.php.net/manual/en/function.array-diff.php |
798 | | - */ |
799 | | - static function pf_arraydiff( Parser &$parser, $arrayId_new, $arrayId1 = null , $arrayId2 = null ) { |
800 | | - $store = self::get( $parser ); |
801 | | - if( ! isset( $arrayId_new ) || ! isset( $arrayId1 ) || ! isset( $arrayId2 ) ) { |
802 | | - return ''; |
803 | | - } |
804 | | - if( ! isset( $arrayId1 ) || ! $store->arrayExists( $arrayId1 ) ) { |
805 | | - return ''; |
806 | | - } |
807 | | - if( ! isset( $arrayId2 ) || ! $store->arrayExists( $arrayId2 ) ) { |
808 | | - return ''; |
809 | | - } |
810 | | - // keys will be preserved... |
811 | | - $newArray = array_diff( array_unique( $store->mArrays[$arrayId1] ), array_unique( $store->mArrays[$arrayId2] ) ); |
812 | | - |
813 | | - // ...so we have to reorganize the key order |
814 | | - $store->mArrays[$arrayId_new] = self::sanitizeArray( $newArray ); |
815 | | - |
816 | | - return ''; |
817 | | - } |
| 749 | + * |
| 750 | + * Usage: |
| 751 | + * {{#arraydiff:arrayid_new |array1 |array2 |... |array n}} |
| 752 | + * |
| 753 | + * Set operation, {white} = {red, white} - {red} |
| 754 | + * See: http://www.php.net/manual/en/function.array-diff.php |
| 755 | + */ |
| 756 | + static function pfObj_arraydiff( &$parser, $frame, $args) { |
| 757 | + self::get( $parser )->multiArrayOperation( $frame, $args, __FUNCTION__, false ); |
| 758 | + return ''; |
| 759 | + } |
| 760 | + private function multi_arraydiff( $array1, $array2 ) { |
| 761 | + // keys will be preserved! |
| 762 | + return array_diff( $array1, $array2 ); |
| 763 | + } |
818 | 764 | |
819 | 765 | |
820 | 766 | ################## |
821 | 767 | # Private helper # |
822 | 768 | ################## |
| 769 | + |
| 770 | + /** |
| 771 | + * Base function for operations with multiple arrays given thru n parameters |
| 772 | + * $operationFunc expects a function name prefix (suffix 'multi_') with two parameters |
| 773 | + * $array1 and $array2 which will perform an action between $array1 and $array2 which |
| 774 | + * will result into a new $array1. There can be 1 to n $hash2 in the whole process. |
| 775 | + * |
| 776 | + * Note: This function is similar to that of Extension:HashTables. |
| 777 | + * |
| 778 | + * @since 2.0 |
| 779 | + * |
| 780 | + * @param $frame PPFrame |
| 781 | + * @param $args array |
| 782 | + * @param $operationFunc string name of the function calling this. There must be a counterpart |
| 783 | + * function with prefix 'multi_' which should have two parameters. Both parameters |
| 784 | + * will receive an array, the function must return the result array of the processing. |
| 785 | + * @param $runFuncOnSingleArray boolean whether the $operationFunc function should be run in case |
| 786 | + * only one array id is given. If not, the original array will end up in the new array. |
| 787 | + */ |
| 788 | + protected function multiArrayOperation( PPFrame $frame, array $args, $operationFunc, $runFuncOnSingleArray = true ) { |
| 789 | + $lastArray = null; |
| 790 | + $operationRan = false; |
| 791 | + $finalArrayId = trim( $frame->expand( $args[0] ) ); |
| 792 | + $operationFunc = 'multi_' . preg_replace( '/^pfObj_/', '', $operationFunc ); |
| 793 | + |
| 794 | + // For all arrays given in parameters 2 to n (ignore 1 because this is the name of the new array) |
| 795 | + for( $i = 1; $i < count( $args ); $i++ ) { |
| 796 | + // just make sure we don't fall into gaps of given arguments: |
| 797 | + if( ! array_key_exists( $i, $args ) ) { |
| 798 | + continue; |
| 799 | + } |
| 800 | + $argArrayId = trim( $frame->expand( $args[ $i ] ) ); |
| 801 | + |
| 802 | + // ignore all tables which do not exist |
| 803 | + if( $this->arrayExists( $argArrayId ) ) { |
| 804 | + $argArray = $this->getArray( $argArrayId ); |
| 805 | + if( $lastArray === null ) { |
| 806 | + // first valid array, process together with second... |
| 807 | + $lastArray = $argArray; |
| 808 | + } |
| 809 | + else { |
| 810 | + // second or later hash table, process with previous: |
| 811 | + $lastArray = $this->{ $operationFunc }( $lastArray, $argArray ); // perform action between last and current array |
| 812 | + $operationRan = true; |
| 813 | + } |
| 814 | + } |
| 815 | + } |
| 816 | + |
| 817 | + // in case no array was given at all: |
| 818 | + if( $lastArray === null ) { |
| 819 | + $lastArray = array(); |
| 820 | + } |
| 821 | + |
| 822 | + global $egArrayExtensionCompatbilityMode; |
| 823 | + |
| 824 | + if( ! $operationRan && $egArrayExtensionCompatbilityMode ) { |
| 825 | + // COMPATBILITY-MODE: |
| 826 | + // Before version 2.0 we didn't create a new array in case only one array was given |
| 827 | + return ''; |
| 828 | + } |
| 829 | + |
| 830 | + // if the operation didn't run because there was only one or no array: |
| 831 | + if( ! $operationRan && $runFuncOnSingleArray ) { |
| 832 | + $lastArray = $this->{ $operationFunc }( $lastArray ); |
| 833 | + } |
| 834 | + |
| 835 | + // re-organize all keys since some 'multi_' functions will preserve keys! |
| 836 | + $lastArray = array_merge( $lastArray ); |
| 837 | + |
| 838 | + $this->setArray( $finalArrayId, $lastArray ); |
| 839 | + } |
823 | 840 | |
824 | 841 | /** |
825 | 842 | * Validates an index for an array and returns true in case the index is a valid index within |
Index: trunk/extensions/ArrayExtension/RELEASE-NOTES |
— | — | @@ -5,11 +5,14 @@ |
6 | 6 | This release is built upon 1.4 alpha. See changes of 1.4 alpha as well. |
7 | 7 | - class 'ArrayExtension' renamed to 'ExtArrayExtension' |
8 | 8 | - '#arrayindex' will only expand options/default when required. |
| 9 | + - '#arraymerge', '#arrayunion', '#arraydiff' and '#arrayintersect' can handle multiple arrays now. |
9 | 10 | - Compatbility mode variable '$egArrayExtensionCompatbilityMode' is set to false by default. See |
10 | 11 | Version 1.4 alpha for further information. Further changes to the compatbility mode behavior |
11 | 12 | in version 2.0: |
12 | 13 | + '#arrayindex' will return its default also in case of existing index but empty value. This |
13 | 14 | makes the function consistent with Variables '#var' and hash tables '#hashvalue'. |
| 15 | + + '#arraymerge', '#arrayunion', '#arraydiff' and '#arrayintersect' with only one array for |
| 16 | + the operation will make a copy of that array instead of creating no array at all. |
14 | 17 | + See 1.4 alpha for previous changes |
15 | 18 | |
16 | 19 | |