@@ -549,32 +549,48 @@ public function isIterableAtLeastOnce(): TrinaryLogic
549
549
550
550
public function removeLast (): self
551
551
{
552
- if (count ($ this ->keyTypes ) === 0 ) {
552
+ return $ this ->removeLastElements (1 );
553
+ }
554
+
555
+ /** @param positive-int $length */
556
+ private function removeLastElements (int $ length ): self
557
+ {
558
+ $ keyTypesCount = count ($ this ->keyTypes );
559
+ if ($ keyTypesCount === 0 ) {
553
560
return $ this ;
554
561
}
555
562
556
- $ i = count ($ this ->keyTypes ) - 1 ;
557
-
558
563
$ keyTypes = $ this ->keyTypes ;
559
564
$ valueTypes = $ this ->valueTypes ;
560
565
$ optionalKeys = $ this ->optionalKeys ;
566
+ $ nextAutoindex = $ this ->nextAutoIndexes ;
561
567
562
- if ($ this ->isOptionalKey ($ i )) {
563
- unset($ optionalKeys [$ i ]);
564
- // Removing the last optional element makes the previous non-optional element optional
565
- for ($ j = $ i - 1 ; $ j >= 0 ; $ j --) {
566
- if (!$ this ->isOptionalKey ($ j )) {
567
- $ optionalKeys [] = $ j ;
568
- break ;
568
+ $ optionalKeysRemoved = 0 ;
569
+ $ newLength = $ keyTypesCount - $ length ;
570
+ for ($ i = $ keyTypesCount - 1 ; $ i >= 0 ; $ i --) {
571
+ $ isOptional = $ this ->isOptionalKey ($ i );
572
+
573
+ if ($ i >= $ newLength ) {
574
+ if ($ isOptional ) {
575
+ $ optionalKeysRemoved ++;
576
+ unset($ optionalKeys [$ i ]);
569
577
}
578
+
579
+ $ removedKeyType = array_pop ($ keyTypes );
580
+ array_pop ($ valueTypes );
581
+ $ nextAutoindex = $ removedKeyType instanceof ConstantIntegerType
582
+ ? $ removedKeyType ->getValue ()
583
+ : $ this ->getNextAutoIndex (); // @phpstan-ignore-line
584
+ continue ;
585
+ }
586
+
587
+ if ($ isOptional || $ optionalKeysRemoved <= 0 ) {
588
+ continue ;
570
589
}
571
- }
572
590
573
- $ removedKeyType = array_pop ($ keyTypes );
574
- array_pop ($ valueTypes );
575
- $ nextAutoindex = $ removedKeyType instanceof ConstantIntegerType
576
- ? $ removedKeyType ->getValue ()
577
- : $ this ->getNextAutoIndex (); // @phpstan-ignore-line
591
+ $ optionalKeys [] = $ i ;
592
+ $ optionalKeysRemoved --;
593
+ }
578
594
579
595
return new self (
580
596
$ keyTypes ,
@@ -584,31 +600,45 @@ public function removeLast(): self
584
600
);
585
601
}
586
602
587
- public function removeFirst (): Type
603
+ public function removeFirst (): self
604
+ {
605
+ return $ this ->removeFirstElements (1 );
606
+ }
607
+
608
+ /** @param positive-int $length */
609
+ private function removeFirstElements (int $ length , bool $ reindex = true ): self
588
610
{
589
611
$ builder = ConstantArrayTypeBuilder::createEmpty ();
590
- $ makeNextNonOptionalOptional = false ;
612
+
613
+ $ optionalKeysIgnored = 0 ;
591
614
foreach ($ this ->keyTypes as $ i => $ keyType ) {
592
615
$ isOptional = $ this ->isOptionalKey ($ i );
593
- if ($ i === 0 ) {
594
- $ makeNextNonOptionalOptional = $ isOptional ;
616
+ if ($ i <= $ length - 1 ) {
617
+ if ($ isOptional ) {
618
+ $ optionalKeysIgnored ++;
619
+ }
595
620
continue ;
596
621
}
597
622
598
- if (!$ isOptional && $ makeNextNonOptionalOptional ) {
623
+ if (!$ isOptional && $ optionalKeysIgnored > 0 ) {
599
624
$ isOptional = true ;
600
- $ makeNextNonOptionalOptional = false ;
625
+ $ optionalKeysIgnored -- ;
601
626
}
602
627
603
628
$ valueType = $ this ->valueTypes [$ i ];
604
- if ($ keyType instanceof ConstantIntegerType) {
629
+ if ($ reindex && $ keyType instanceof ConstantIntegerType) {
605
630
$ keyType = null ;
606
631
}
607
632
608
633
$ builder ->setOffsetValueType ($ keyType , $ valueType , $ isOptional );
609
634
}
610
635
611
- return $ builder ->getArray ();
636
+ $ array = $ builder ->getArray ();
637
+ if (!$ array instanceof self) {
638
+ throw new ShouldNotHappenException ();
639
+ }
640
+
641
+ return $ array ;
612
642
}
613
643
614
644
public function slice (int $ offset , ?int $ limit , bool $ preserveKeys = false ): self
0 commit comments