3434use function str_repeat ;
3535
3636/**
37- * @phpstan-implements \IteratorAggregate<int, Tag>
37+ * @phpstan-template TValue of Tag = Tag
38+ * @phpstan-implements \IteratorAggregate<int, TValue>
3839 */
3940final class ListTag extends Tag implements \Countable, \IteratorAggregate{
4041 use NoDynamicFieldsTrait;
@@ -43,12 +44,13 @@ final class ListTag extends Tag implements \Countable, \IteratorAggregate{
4344 private $ tagType ;
4445 /**
4546 * @var \SplDoublyLinkedList|Tag[]
46- * @phpstan-var \SplDoublyLinkedList<Tag >
47+ * @phpstan-var \SplDoublyLinkedList<TValue >
4748 */
4849 private $ value ;
4950
5051 /**
5152 * @param Tag[] $value
53+ * @phpstan-param TValue[] $value
5254 */
5355 public function __construct (array $ value = [], int $ tagType = NBT ::TAG_End){
5456 self ::restrictArgCount (__METHOD__ , func_num_args (), 2 );
@@ -61,7 +63,7 @@ public function __construct(array $value = [], int $tagType = NBT::TAG_End){
6163
6264 /**
6365 * @return Tag[]
64- * @phpstan-return list<Tag >
66+ * @phpstan-return list<TValue >
6567 */
6668 public function getValue () : array {
6769 $ value = [];
@@ -86,6 +88,34 @@ public function getAllValues() : array{
8688 return $ result ;
8789 }
8890
91+ /**
92+ * @phpstan-template TTarget of Tag
93+ * @phpstan-param class-string<TTarget> $tagClass
94+ * @phpstan-this-out self<TTarget> $this
95+ */
96+ private function checkTagClass (string $ tagClass ) : bool {
97+ return $ this ->value ->isEmpty () ?
98+ $ this ->tagType === NBT ::TAG_End :
99+ $ this ->first () instanceof $ tagClass ;
100+ }
101+
102+ /**
103+ * Returns $this if the tag values are of type $tagClass, null otherwise.
104+ * The returned value will have the proper PHPStan generic types set if it matches.
105+ *
106+ * If the list is empty and is of TAG_End type, the cast will always succeed, since TAG_End lists
107+ * will have their type updated as soon as a value is inserted. However, casting any other type of
108+ * empty list will fail if the type is incorrect.
109+ *
110+ * @phpstan-template TTarget of Tag
111+ * @phpstan-param class-string<TTarget> $tagClass
112+ *
113+ * @phpstan-return self<TTarget>|null
114+ */
115+ public function cast (string $ tagClass ) : ?self {
116+ return $ this ->checkTagClass ($ tagClass ) ? $ this : null ;
117+ }
118+
89119 public function count () : int {
90120 return $ this ->value ->count ();
91121 }
@@ -96,6 +126,8 @@ public function getCount() : int{
96126
97127 /**
98128 * Appends the specified tag to the end of the list.
129+ *
130+ * @phpstan-param TValue $tag
99131 */
100132 public function push (Tag $ tag ) : void {
101133 $ this ->checkTagType ($ tag );
@@ -104,13 +136,16 @@ public function push(Tag $tag) : void{
104136
105137 /**
106138 * Removes the last tag from the list and returns it.
139+ * @phpstan-return TValue
107140 */
108141 public function pop () : Tag {
109142 return $ this ->value ->pop ();
110143 }
111144
112145 /**
113146 * Adds the specified tag to the start of the list.
147+ *
148+ * @phpstan-param TValue $tag
114149 */
115150 public function unshift (Tag $ tag ) : void {
116151 $ this ->checkTagType ($ tag );
@@ -119,6 +154,7 @@ public function unshift(Tag $tag) : void{
119154
120155 /**
121156 * Removes the first tag from the list and returns it.
157+ * @phpstan-return TValue
122158 */
123159 public function shift () : Tag {
124160 return $ this ->value ->shift ();
@@ -128,6 +164,8 @@ public function shift() : Tag{
128164 * Inserts a tag into the list between existing tags, at the specified offset. Later values in the list are moved up
129165 * by 1 position.
130166 *
167+ * @phpstan-param TValue $tag
168+ *
131169 * @return void
132170 * @throws \OutOfRangeException if the offset is not within the bounds of the list
133171 */
@@ -146,6 +184,8 @@ public function remove(int $offset) : void{
146184 /**
147185 * Returns the tag at the specified offset.
148186 *
187+ * @phpstan-return TValue
188+ *
149189 * @throws \OutOfRangeException if the offset is not within the bounds of the list
150190 */
151191 public function get (int $ offset ) : Tag {
@@ -157,13 +197,15 @@ public function get(int $offset) : Tag{
157197
158198 /**
159199 * Returns the element in the first position of the list, without removing it.
200+ * @phpstan-return TValue
160201 */
161202 public function first () : Tag {
162203 return $ this ->value ->bottom ();
163204 }
164205
165206 /**
166207 * Returns the element in the last position in the list (the end), without removing it.
208+ * @phpstan-return TValue
167209 */
168210 public function last () : Tag {
169211 return $ this ->value ->top ();
@@ -172,6 +214,8 @@ public function last() : Tag{
172214 /**
173215 * Overwrites the tag at the specified offset.
174216 *
217+ * @phpstan-param TValue $tag
218+ *
175219 * @throws \OutOfRangeException if the offset is not within the bounds of the list
176220 */
177221 public function set (int $ offset , Tag $ tag ) : void {
@@ -209,6 +253,7 @@ public function getTagType() : int{
209253 }
210254
211255 /**
256+ * @deprecated
212257 * Sets the type of tag that can be added to this list. If TAG_End is used, the type will be auto-detected from the
213258 * first tag added to the list.
214259 *
@@ -263,23 +308,21 @@ public static function read(NbtStreamReader $reader, ReaderTracker $tracker) : s
263308 public function write (NbtStreamWriter $ writer ) : void {
264309 $ writer ->writeByte ($ this ->tagType );
265310 $ writer ->writeInt ($ this ->value ->count ());
266- /** @var Tag $tag */
267311 foreach ($ this ->value as $ tag ){
268312 $ tag ->write ($ writer );
269313 }
270314 }
271315
272316 protected function stringifyValue (int $ indentation ) : string {
273317 $ str = "{ \n" ;
274- /** @var Tag $tag */
275318 foreach ($ this ->value as $ tag ){
276319 $ str .= str_repeat (" " , $ indentation + 1 ) . $ tag ->toString ($ indentation + 1 ) . "\n" ;
277320 }
278321 return $ str . str_repeat (" " , $ indentation ) . "} " ;
279322 }
280323
281324 public function __clone (){
282- /** @phpstan-var \SplDoublyLinkedList<Tag > $new */
325+ /** @phpstan-var \SplDoublyLinkedList<TValue > $new */
283326 $ new = new \SplDoublyLinkedList ();
284327
285328 foreach ($ this ->value as $ tag ){
@@ -295,7 +338,7 @@ protected function makeCopy(){
295338
296339 /**
297340 * @return \Generator|Tag[]
298- * @phpstan-return \Generator<int, Tag , void, void>
341+ * @phpstan-return \Generator<int, TValue , void, void>
299342 */
300343 public function getIterator () : \Generator {
301344 //we technically don't need iterator_to_array() here, but I don't feel comfortable relying on "yield from" to
0 commit comments