├── .editorconfig ├── .gitignore ├── LICENSE.md ├── README.md ├── basic ├── 1.php └── README.md ├── beginner ├── 1.php ├── 2.php ├── 3.php ├── 4.php └── README.md ├── composer.json ├── composer.lock ├── phpstan.dist.neon └── pictures ├── highlighting-comments.png └── splited-screen.png /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | charset = utf-8 11 | 12 | # PHP - http://php.net/ 13 | [*.php] 14 | indent_style = tab 15 | indent_size = 4 16 | 17 | # NEON - https://ne-on.org/ 18 | [{*.neon,*.neon.dist}] 19 | indent_style = tab 20 | indent_size = 4 21 | 22 | # CommonMark - https://commonmark.org/ 23 | # GitHub Flavored Markdown Spec - https://github.github.com/gfm/ 24 | [*.md] 25 | trim_trailing_whitespace = false 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /cache/ 2 | /resultCache.php 3 | /vendor/* 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # GNU Free Documentation License 2 | 3 | Version 1.3, 3 November 2008 4 | 5 | Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, 6 | Inc. 7 | 8 | Everyone is permitted to copy and distribute verbatim copies of this 9 | license document, but changing it is not allowed. 10 | 11 | ## 0. PREAMBLE 12 | 13 | The purpose of this License is to make a manual, textbook, or other 14 | functional and useful document "free" in the sense of freedom: to 15 | assure everyone the effective freedom to copy and redistribute it, 16 | with or without modifying it, either commercially or noncommercially. 17 | Secondarily, this License preserves for the author and publisher a way 18 | to get credit for their work, while not being considered responsible 19 | for modifications made by others. 20 | 21 | This License is a kind of "copyleft", which means that derivative 22 | works of the document must themselves be free in the same sense. It 23 | complements the GNU General Public License, which is a copyleft 24 | license designed for free software. 25 | 26 | We have designed this License in order to use it for manuals for free 27 | software, because free software needs free documentation: a free 28 | program should come with manuals providing the same freedoms that the 29 | software does. But this License is not limited to software manuals; it 30 | can be used for any textual work, regardless of subject matter or 31 | whether it is published as a printed book. We recommend this License 32 | principally for works whose purpose is instruction or reference. 33 | 34 | ## 1. APPLICABILITY AND DEFINITIONS 35 | 36 | This License applies to any manual or other work, in any medium, that 37 | contains a notice placed by the copyright holder saying it can be 38 | distributed under the terms of this License. Such a notice grants a 39 | world-wide, royalty-free license, unlimited in duration, to use that 40 | work under the conditions stated herein. The "Document", below, refers 41 | to any such manual or work. Any member of the public is a licensee, 42 | and is addressed as "you". You accept the license if you copy, modify 43 | or distribute the work in a way requiring permission under copyright 44 | law. 45 | 46 | A "Modified Version" of the Document means any work containing the 47 | Document or a portion of it, either copied verbatim, or with 48 | modifications and/or translated into another language. 49 | 50 | A "Secondary Section" is a named appendix or a front-matter section of 51 | the Document that deals exclusively with the relationship of the 52 | publishers or authors of the Document to the Document's overall 53 | subject (or to related matters) and contains nothing that could fall 54 | directly within that overall subject. (Thus, if the Document is in 55 | part a textbook of mathematics, a Secondary Section may not explain 56 | any mathematics.) The relationship could be a matter of historical 57 | connection with the subject or with related matters, or of legal, 58 | commercial, philosophical, ethical or political position regarding 59 | them. 60 | 61 | The "Invariant Sections" are certain Secondary Sections whose titles 62 | are designated, as being those of Invariant Sections, in the notice 63 | that says that the Document is released under this License. If a 64 | section does not fit the above definition of Secondary then it is not 65 | allowed to be designated as Invariant. The Document may contain zero 66 | Invariant Sections. If the Document does not identify any Invariant 67 | Sections then there are none. 68 | 69 | The "Cover Texts" are certain short passages of text that are listed, 70 | as Front-Cover Texts or Back-Cover Texts, in the notice that says that 71 | the Document is released under this License. A Front-Cover Text may be 72 | at most 5 words, and a Back-Cover Text may be at most 25 words. 73 | 74 | A "Transparent" copy of the Document means a machine-readable copy, 75 | represented in a format whose specification is available to the 76 | general public, that is suitable for revising the document 77 | straightforwardly with generic text editors or (for images composed of 78 | pixels) generic paint programs or (for drawings) some widely available 79 | drawing editor, and that is suitable for input to text formatters or 80 | for automatic translation to a variety of formats suitable for input 81 | to text formatters. A copy made in an otherwise Transparent file 82 | format whose markup, or absence of markup, has been arranged to thwart 83 | or discourage subsequent modification by readers is not Transparent. 84 | An image format is not Transparent if used for any substantial amount 85 | of text. A copy that is not "Transparent" is called "Opaque". 86 | 87 | Examples of suitable formats for Transparent copies include plain 88 | ASCII without markup, Texinfo input format, LaTeX input format, SGML 89 | or XML using a publicly available DTD, and standard-conforming simple 90 | HTML, PostScript or PDF designed for human modification. Examples of 91 | transparent image formats include PNG, XCF and JPG. Opaque formats 92 | include proprietary formats that can be read and edited only by 93 | proprietary word processors, SGML or XML for which the DTD and/or 94 | processing tools are not generally available, and the 95 | machine-generated HTML, PostScript or PDF produced by some word 96 | processors for output purposes only. 97 | 98 | The "Title Page" means, for a printed book, the title page itself, 99 | plus such following pages as are needed to hold, legibly, the material 100 | this License requires to appear in the title page. For works in 101 | formats which do not have any title page as such, "Title Page" means 102 | the text near the most prominent appearance of the work's title, 103 | preceding the beginning of the body of the text. 104 | 105 | The "publisher" means any person or entity that distributes copies of 106 | the Document to the public. 107 | 108 | A section "Entitled XYZ" means a named subunit of the Document whose 109 | title either is precisely XYZ or contains XYZ in parentheses following 110 | text that translates XYZ in another language. (Here XYZ stands for a 111 | specific section name mentioned below, such as "Acknowledgements", 112 | "Dedications", "Endorsements", or "History".) To "Preserve the Title" 113 | of such a section when you modify the Document means that it remains a 114 | section "Entitled XYZ" according to this definition. 115 | 116 | The Document may include Warranty Disclaimers next to the notice which 117 | states that this License applies to the Document. These Warranty 118 | Disclaimers are considered to be included by reference in this 119 | License, but only as regards disclaiming warranties: any other 120 | implication that these Warranty Disclaimers may have is void and has 121 | no effect on the meaning of this License. 122 | 123 | ## 2. VERBATIM COPYING 124 | 125 | You may copy and distribute the Document in any medium, either 126 | commercially or noncommercially, provided that this License, the 127 | copyright notices, and the license notice saying this License applies 128 | to the Document are reproduced in all copies, and that you add no 129 | other conditions whatsoever to those of this License. You may not use 130 | technical measures to obstruct or control the reading or further 131 | copying of the copies you make or distribute. However, you may accept 132 | compensation in exchange for copies. If you distribute a large enough 133 | number of copies you must also follow the conditions in section 3. 134 | 135 | You may also lend copies, under the same conditions stated above, and 136 | you may publicly display copies. 137 | 138 | ## 3. COPYING IN QUANTITY 139 | 140 | If you publish printed copies (or copies in media that commonly have 141 | printed covers) of the Document, numbering more than 100, and the 142 | Document's license notice requires Cover Texts, you must enclose the 143 | copies in covers that carry, clearly and legibly, all these Cover 144 | Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on 145 | the back cover. Both covers must also clearly and legibly identify you 146 | as the publisher of these copies. The front cover must present the 147 | full title with all words of the title equally prominent and visible. 148 | You may add other material on the covers in addition. Copying with 149 | changes limited to the covers, as long as they preserve the title of 150 | the Document and satisfy these conditions, can be treated as verbatim 151 | copying in other respects. 152 | 153 | If the required texts for either cover are too voluminous to fit 154 | legibly, you should put the first ones listed (as many as fit 155 | reasonably) on the actual cover, and continue the rest onto adjacent 156 | pages. 157 | 158 | If you publish or distribute Opaque copies of the Document numbering 159 | more than 100, you must either include a machine-readable Transparent 160 | copy along with each Opaque copy, or state in or with each Opaque copy 161 | a computer-network location from which the general network-using 162 | public has access to download using public-standard network protocols 163 | a complete Transparent copy of the Document, free of added material. 164 | If you use the latter option, you must take reasonably prudent steps, 165 | when you begin distribution of Opaque copies in quantity, to ensure 166 | that this Transparent copy will remain thus accessible at the stated 167 | location until at least one year after the last time you distribute an 168 | Opaque copy (directly or through your agents or retailers) of that 169 | edition to the public. 170 | 171 | It is requested, but not required, that you contact the authors of the 172 | Document well before redistributing any large number of copies, to 173 | give them a chance to provide you with an updated version of the 174 | Document. 175 | 176 | ## 4. MODIFICATIONS 177 | 178 | You may copy and distribute a Modified Version of the Document under 179 | the conditions of sections 2 and 3 above, provided that you release 180 | the Modified Version under precisely this License, with the Modified 181 | Version filling the role of the Document, thus licensing distribution 182 | and modification of the Modified Version to whoever possesses a copy 183 | of it. In addition, you must do these things in the Modified Version: 184 | 185 | - A. Use in the Title Page (and on the covers, if any) a title 186 | distinct from that of the Document, and from those of previous 187 | versions (which should, if there were any, be listed in the 188 | History section of the Document). You may use the same title as a 189 | previous version if the original publisher of that version 190 | gives permission. 191 | - B. List on the Title Page, as authors, one or more persons or 192 | entities responsible for authorship of the modifications in the 193 | Modified Version, together with at least five of the principal 194 | authors of the Document (all of its principal authors, if it has 195 | fewer than five), unless they release you from this requirement. 196 | - C. State on the Title page the name of the publisher of the 197 | Modified Version, as the publisher. 198 | - D. Preserve all the copyright notices of the Document. 199 | - E. Add an appropriate copyright notice for your modifications 200 | adjacent to the other copyright notices. 201 | - F. Include, immediately after the copyright notices, a license 202 | notice giving the public permission to use the Modified Version 203 | under the terms of this License, in the form shown in the 204 | Addendum below. 205 | - G. Preserve in that license notice the full lists of Invariant 206 | Sections and required Cover Texts given in the Document's 207 | license notice. 208 | - H. Include an unaltered copy of this License. 209 | - I. Preserve the section Entitled "History", Preserve its Title, 210 | and add to it an item stating at least the title, year, new 211 | authors, and publisher of the Modified Version as given on the 212 | Title Page. If there is no section Entitled "History" in the 213 | Document, create one stating the title, year, authors, and 214 | publisher of the Document as given on its Title Page, then add an 215 | item describing the Modified Version as stated in the 216 | previous sentence. 217 | - J. Preserve the network location, if any, given in the Document 218 | for public access to a Transparent copy of the Document, and 219 | likewise the network locations given in the Document for previous 220 | versions it was based on. These may be placed in the "History" 221 | section. You may omit a network location for a work that was 222 | published at least four years before the Document itself, or if 223 | the original publisher of the version it refers to 224 | gives permission. 225 | - K. For any section Entitled "Acknowledgements" or "Dedications", 226 | Preserve the Title of the section, and preserve in the section all 227 | the substance and tone of each of the contributor acknowledgements 228 | and/or dedications given therein. 229 | - L. Preserve all the Invariant Sections of the Document, unaltered 230 | in their text and in their titles. Section numbers or the 231 | equivalent are not considered part of the section titles. 232 | - M. Delete any section Entitled "Endorsements". Such a section may 233 | not be included in the Modified Version. 234 | - N. Do not retitle any existing section to be Entitled 235 | "Endorsements" or to conflict in title with any Invariant Section. 236 | - O. Preserve any Warranty Disclaimers. 237 | 238 | If the Modified Version includes new front-matter sections or 239 | appendices that qualify as Secondary Sections and contain no material 240 | copied from the Document, you may at your option designate some or all 241 | of these sections as invariant. To do this, add their titles to the 242 | list of Invariant Sections in the Modified Version's license notice. 243 | These titles must be distinct from any other section titles. 244 | 245 | You may add a section Entitled "Endorsements", provided it contains 246 | nothing but endorsements of your Modified Version by various 247 | parties—for example, statements of peer review or that the text has 248 | been approved by an organization as the authoritative definition of a 249 | standard. 250 | 251 | You may add a passage of up to five words as a Front-Cover Text, and a 252 | passage of up to 25 words as a Back-Cover Text, to the end of the list 253 | of Cover Texts in the Modified Version. Only one passage of 254 | Front-Cover Text and one of Back-Cover Text may be added by (or 255 | through arrangements made by) any one entity. If the Document already 256 | includes a cover text for the same cover, previously added by you or 257 | by arrangement made by the same entity you are acting on behalf of, 258 | you may not add another; but you may replace the old one, on explicit 259 | permission from the previous publisher that added the old one. 260 | 261 | The author(s) and publisher(s) of the Document do not by this License 262 | give permission to use their names for publicity for or to assert or 263 | imply endorsement of any Modified Version. 264 | 265 | ## 5. COMBINING DOCUMENTS 266 | 267 | You may combine the Document with other documents released under this 268 | License, under the terms defined in section 4 above for modified 269 | versions, provided that you include in the combination all of the 270 | Invariant Sections of all of the original documents, unmodified, and 271 | list them all as Invariant Sections of your combined work in its 272 | license notice, and that you preserve all their Warranty Disclaimers. 273 | 274 | The combined work need only contain one copy of this License, and 275 | multiple identical Invariant Sections may be replaced with a single 276 | copy. If there are multiple Invariant Sections with the same name but 277 | different contents, make the title of each such section unique by 278 | adding at the end of it, in parentheses, the name of the original 279 | author or publisher of that section if known, or else a unique number. 280 | Make the same adjustment to the section titles in the list of 281 | Invariant Sections in the license notice of the combined work. 282 | 283 | In the combination, you must combine any sections Entitled "History" 284 | in the various original documents, forming one section Entitled 285 | "History"; likewise combine any sections Entitled "Acknowledgements", 286 | and any sections Entitled "Dedications". You must delete all sections 287 | Entitled "Endorsements". 288 | 289 | ## 6. COLLECTIONS OF DOCUMENTS 290 | 291 | You may make a collection consisting of the Document and other 292 | documents released under this License, and replace the individual 293 | copies of this License in the various documents with a single copy 294 | that is included in the collection, provided that you follow the rules 295 | of this License for verbatim copying of each of the documents in all 296 | other respects. 297 | 298 | You may extract a single document from such a collection, and 299 | distribute it individually under this License, provided you insert a 300 | copy of this License into the extracted document, and follow this 301 | License in all other respects regarding verbatim copying of that 302 | document. 303 | 304 | ## 7. AGGREGATION WITH INDEPENDENT WORKS 305 | 306 | A compilation of the Document or its derivatives with other separate 307 | and independent documents or works, in or on a volume of a storage or 308 | distribution medium, is called an "aggregate" if the copyright 309 | resulting from the compilation is not used to limit the legal rights 310 | of the compilation's users beyond what the individual works permit. 311 | When the Document is included in an aggregate, this License does not 312 | apply to the other works in the aggregate which are not themselves 313 | derivative works of the Document. 314 | 315 | If the Cover Text requirement of section 3 is applicable to these 316 | copies of the Document, then if the Document is less than one half of 317 | the entire aggregate, the Document's Cover Texts may be placed on 318 | covers that bracket the Document within the aggregate, or the 319 | electronic equivalent of covers if the Document is in electronic form. 320 | Otherwise they must appear on printed covers that bracket the whole 321 | aggregate. 322 | 323 | ## 8. TRANSLATION 324 | 325 | Translation is considered a kind of modification, so you may 326 | distribute translations of the Document under the terms of section 4. 327 | Replacing Invariant Sections with translations requires special 328 | permission from their copyright holders, but you may include 329 | translations of some or all Invariant Sections in addition to the 330 | original versions of these Invariant Sections. You may include a 331 | translation of this License, and all the license notices in the 332 | Document, and any Warranty Disclaimers, provided that you also include 333 | the original English version of this License and the original versions 334 | of those notices and disclaimers. In case of a disagreement between 335 | the translation and the original version of this License or a notice 336 | or disclaimer, the original version will prevail. 337 | 338 | If a section in the Document is Entitled "Acknowledgements", 339 | "Dedications", or "History", the requirement (section 4) to Preserve 340 | its Title (section 1) will typically require changing the actual 341 | title. 342 | 343 | ## 9. TERMINATION 344 | 345 | You may not copy, modify, sublicense, or distribute the Document 346 | except as expressly provided under this License. Any attempt otherwise 347 | to copy, modify, sublicense, or distribute it is void, and will 348 | automatically terminate your rights under this License. 349 | 350 | However, if you cease all violation of this License, then your license 351 | from a particular copyright holder is reinstated (a) provisionally, 352 | unless and until the copyright holder explicitly and finally 353 | terminates your license, and (b) permanently, if the copyright holder 354 | fails to notify you of the violation by some reasonable means prior to 355 | 60 days after the cessation. 356 | 357 | Moreover, your license from a particular copyright holder is 358 | reinstated permanently if the copyright holder notifies you of the 359 | violation by some reasonable means, this is the first time you have 360 | received notice of violation of this License (for any work) from that 361 | copyright holder, and you cure the violation prior to 30 days after 362 | your receipt of the notice. 363 | 364 | Termination of your rights under this section does not terminate the 365 | licenses of parties who have received copies or rights from you under 366 | this License. If your rights have been terminated and not permanently 367 | reinstated, receipt of a copy of some or all of the same material does 368 | not give you any rights to use it. 369 | 370 | ## 10. FUTURE REVISIONS OF THIS LICENSE 371 | 372 | The Free Software Foundation may publish new, revised versions of the 373 | GNU Free Documentation License from time to time. Such new versions 374 | will be similar in spirit to the present version, but may differ in 375 | detail to address new problems or concerns. See 376 | . 377 | 378 | Each version of the License is given a distinguishing version number. 379 | If the Document specifies that a particular numbered version of this 380 | License "or any later version" applies to it, you have the option of 381 | following the terms and conditions either of that specified version or 382 | of any later version that has been published (not as a draft) by the 383 | Free Software Foundation. If the Document does not specify a version 384 | number of this License, you may choose any version ever published (not 385 | as a draft) by the Free Software Foundation. If the Document specifies 386 | that a proxy can decide which future versions of this License can be 387 | used, that proxy's public statement of acceptance of a version 388 | permanently authorizes you to choose that version for the Document. 389 | 390 | ## 11. RELICENSING 391 | 392 | "Massive Multiauthor Collaboration Site" (or "MMC Site") means any 393 | World Wide Web server that publishes copyrightable works and also 394 | provides prominent facilities for anybody to edit those works. A 395 | public wiki that anybody can edit is an example of such a server. A 396 | "Massive Multiauthor Collaboration" (or "MMC") contained in the site 397 | means any set of copyrightable works thus published on the MMC site. 398 | 399 | "CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 400 | license published by Creative Commons Corporation, a not-for-profit 401 | corporation with a principal place of business in San Francisco, 402 | California, as well as future copyleft versions of that license 403 | published by that same organization. 404 | 405 | "Incorporate" means to publish or republish a Document, in whole or in 406 | part, as part of another Document. 407 | 408 | An MMC is "eligible for relicensing" if it is licensed under this 409 | License, and if all works that were first published under this License 410 | somewhere other than this MMC, and subsequently incorporated in whole 411 | or in part into the MMC, (1) had no cover texts or invariant sections, 412 | and (2) were thus incorporated prior to November 1, 2008. 413 | 414 | The operator of an MMC Site may republish an MMC contained in the site 415 | under CC-BY-SA on the same site at any time before August 1, 2009, 416 | provided the MMC is eligible for relicensing. 417 | 418 | ## ADDENDUM: How to use this License for your documents 419 | 420 | To use this License in a document you have written, include a copy of 421 | the License in the document and put the following copyright and 422 | license notices just after the title page: 423 | 424 | Copyright (C) YEAR YOUR NAME. 425 | Permission is granted to copy, distribute and/or modify this document 426 | under the terms of the GNU Free Documentation License, Version 1.3 427 | or any later version published by the Free Software Foundation; 428 | with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. 429 | A copy of the license is included in the section entitled "GNU 430 | Free Documentation License". 431 | 432 | If you have Invariant Sections, Front-Cover Texts and Back-Cover 433 | Texts, replace the "with … Texts." line with this: 434 | 435 | with the Invariant Sections being LIST THEIR TITLES, with the 436 | Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. 437 | 438 | If you have Invariant Sections without Cover Texts, or some other 439 | combination of the three, merge those two alternatives to suit the 440 | situation. 441 | 442 | If your document contains nontrivial examples of program code, we 443 | recommend releasing these examples in parallel under your choice of 444 | free software license, such as the GNU General Public License, to 445 | permit their use in free software. 446 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHPStan型付けチュートリアル 2 | 3 | PHPStanを使った型付けをWebブラウザ上で体験できるチュートリアルです。 4 | 5 | コード編集中にリアルタイムでPHPStanのフィードバックを得て「**PHPStanとペアプログラミングをする**」感覚を獲得してください。 6 | 7 | ## チュートリアル 8 | 9 | 1. 🌱 [**PHPStan型付けチュートリアル 入門編**](beginner/README.md) 10 | 2. 🔰 [**PHPStan型付けチュートリアル 基礎編**](basic/README.md) ***(工事中)*** 11 | 12 | ## 取り組み方 13 | 14 | チュートリアルには節見出しごとに、以下のようなブロックがあります。 15 | 16 | > [!NOTE] 17 | > この節のコードは以下で確認できます 18 | > * **PHPStan Playground**: `https://phpstan.org/...` 19 | > * **File**: `xxx.php` 20 | > * **CLI**: `./vendor/bin/phpstan analyze beginner/xxx.php` 21 | 22 | ### 🖥️ Webブラウザで取り組む 23 | 24 | チュートリアル本文にある **PHPStan Playground**: `https://phpstan.org/...` のリンクからWeb上のPlaygroundにアクセスできます。 25 | 26 | Webブラウザのウィンドウを分割し、記事本文とPHPStan Playgroundを並べて表示すると便利です。 27 | 28 | ![](./pictures/splited-screen.png) 29 | 30 | ### ローカルで実行する 31 | 32 | このGitリポジトリをローカルに `git clone` してください。 33 | 34 | > [!TIP] 35 | > PHP 8.0以降と[Composer]がインストールされている必要があります。 36 | > `git clone` 後にディレクトリ内に移動し `composer install` を実行してください。 37 | 38 | #### 📜 エディタ上から実行する 39 | 40 | チュートリアルを読み進めながら、同じディレクトリがあるファイルを編集してください。 41 | 42 | > [!TIP] 43 | > エディタ画面で編集中にPHPStanの出力を表示できるようにすることを **強く推奨** します。 44 | > **PhpStorm**、**VS Code**、**GNU Emacs**、**Vim**などでは拡張を有効化することで実現できます。 45 | 46 | > [!WARNING] 47 | > エディタ内からリアルタイムでPHPStanを実行する環境が用意できない場合は**Webブラウザを使ってください**。 48 | 49 | [Composer]: https://getcomposer.org/ 50 | 51 | #### **非推奨** 🚫 ターミナルから実行する 52 | 53 | このチュートリアルは、エディタ画面から透過的にPHPStanを実行することが理想です。 54 | 55 | どうしても実行できない場合は端末から**CLI**で指定されている`./vendor/bin/phpstan analyze beginner/xxx.php`のようなコマンドを実行してください。 56 | 57 | ## Copyright 58 | 59 | この文書は[GNU自由文書ライセンス]により自由に利用できます。 60 | 61 | Copyright (c) 2025 USAMI Kenta 62 | Permission is granted to copy, distribute and/or modify this document 63 | under the terms of the GNU Free Documentation License, Version 1.3 64 | or any later version published by the Free Software Foundation; 65 | with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. 66 | A copy of the license is included in the section entitled "GNU 67 | Free Documentation License". 68 | 69 | コード部分については[BSD Zero Clause License]とします。 70 | 71 | BSD Zero Clause License 72 | 73 | Copyright (c) 2025 USAMI Kenta 74 | 75 | Permission to use, copy, modify, and/or distribute this software for any 76 | purpose with or without fee is hereby granted. 77 | 78 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 79 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 80 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 81 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 82 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 83 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 84 | PERFORMANCE OF THIS SOFTWARE. 85 | 86 | [GNU自由文書ライセンス]: https://www.gnu.org/licenses/fdl-1.3.html 87 | [gfdl-ja]: https://doclicenses.opensource.jp/GFDL-1.2/GFDL-1.2.html 88 | [BSD Zero Clause License]: https://choosealicense.com/licenses/0bsd/ 89 | -------------------------------------------------------------------------------- /basic/1.php: -------------------------------------------------------------------------------- 1 | $id, 9 | 'Name' => $name, 10 | 'BirthDay' => new DateTimeImmutable($birthday), 11 | ]; 12 | 13 | return $result; 14 | } 15 | 16 | public function fetchUsers(): array 17 | { 18 | // 仮実装なので仮データを返す 19 | $users = []; 20 | $users[] = $this->buildUser(1, 'Miku', '2007-08-31'); 21 | $users[] = $this->buildUser(2, 'Rin', '2007-12-27'); 22 | $users[] = $this->buildUser(3, 'Len', '2007-12-27'); 23 | $users[] = $this->buildUser(4, 'Luka', '2009-01-30'); 24 | 25 | return $users; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /basic/README.md: -------------------------------------------------------------------------------- 1 | # PHPStan型付けチュートリアル 基礎編 2 | 3 | 4 | ## 1. 関数の戻り値に型をつけよう 5 | 6 | 既存の関数には適切な戻り値をつけることが大切ですが、型を付けるのに必要以上に手間をかけるのは徒労です。 7 | 8 | このコースでは極力手間をかけずに型を書くことを身につけましょう。 9 | 10 | > [!NOTE] 11 | > この節のコードは以下で確認できます 12 | > * **PHPStan Playground**: 13 | > * **File**: [`1.php`](./1.php) 14 | > * **CLI**: `./vendor/bin/phpstan analyze basic/1.php` 15 | 16 | ``` php 17 | $id, 25 | 'Name' => $name, 26 | 'BirthDay' => new DateTimeImmutable($birthday), 27 | ]; 28 | 29 | return $result; 30 | } 31 | 32 | public function fetchUsers(): array 33 | { 34 | // 仮実装なので仮データを返す 35 | $users = []; 36 | $users[] = $this->buildUser(1, 'Miku', '2007-08-31'); 37 | $users[] = $this->buildUser(2, 'Rin', '2007-12-27'); 38 | $users[] = $this->buildUser(3, 'Len', '2007-12-27'); 39 | $users[] = $this->buildUser(4, 'Luka', '2009-01-30'); 40 | 41 | return $users; 42 | } 43 | } 44 | ``` 45 | 46 | 初期状態ではPHPStanは以下のようなエラーを発生させます。 47 | 48 | > [!WARNING] 49 | > 50 | > ``` 51 | > Method UsersBuilder::buildUser() return type has no value type specified in iterable type array. 52 | > Method UsersBuilder::fetchUsers() return type has no value type specified in iterable type array. 53 | > ``` 54 | > **See**: [Solving PHPStan error "No value type specified in iterable type"](https://phpstan.org/blog/solving-phpstan-no-value-type-specified-in-iterable-type) 55 | 56 | PHPStanは基本的に `array` だけの型宣言には厳しい対応をします。なぜなら、`array`を一皮剥いたら何が返ってくるかわからない、つまりは**型なし**だからです。そのため、配列の内部構造も公開仕様として語ってあげなければいけません。 57 | 58 | PHPDocで配列に詳細な型を記述する方法は大きく分けて二つあります: 59 | 60 | * `array`, `array` (ジェネリック配列記法) 61 | * 同じ構造が繰り返す場合に使う 62 | * `array{}`, `array{key: value}`, `array{int, string, string}` (array-shapes記法) 63 | * 配列の中のキーごとに別の意味の値が格納される場合に使う 64 | 65 | 使い慣れてくればどちらがどちらか混乱することはないのですが大変初心者殺しであることは間違いありません。 66 | 67 | そこで使えるのが `\PHPStan\dumpPhpDocType()` です。これは `\PHPStan\dumpType()` と似ていますが、PHPDocでそのまま使える記法で出力してくれる機能です。 68 | 69 | 手始めに `buildUser()` メソッドに型をつけてみましょう。 70 | 71 | ```php 72 | public function buildUser(int $id, string $name, string $birthday): array 73 | { 74 | $result = [ 75 | 'ID' => $id, 76 | 'Name' => $name, 77 | 'BirthDay' => new DateTimeImmutable($birthday), 78 | ]; 79 | \PHPStan\dumpPhpDocType($result); 80 | # Dumped type: array{ID: int, Name: string, BirthDay: DateTimeImmutable} 81 | 82 | return $result; 83 | } 84 | ``` 85 | 86 | 型が出力されました。これをコピペしてPHPDocに貼り付けます。 87 | 88 | ```php 89 | /** 90 | * @return array{ID: int, Name: string, BirthDay: DateTimeImmutable} 91 | */ 92 | ``` 93 | 94 | 今度は同じように `fetchUsers()` に型をつけてみましょう。 95 | 96 | うまく行っていれば、こういう型が出力されるはずです。 97 | 98 | ``` 99 | Dumped type: array{array{ID: int, Name: string, BirthDay: DateTimeImmutable}, array{ID: int, Name: string, BirthDay: DateTimeImmutable}, array{ID: int, Name: string, BirthDay: DateTimeImmutable}, array{ID: int, Name: string, BirthDay: DateTimeImmutable}} 100 | ``` 101 | 102 | なんだか長い型が出てしまいましたね… これはPHPStanが賢すぎて、`$users`がきちんと長さ4の配列だと認識してくれてしまっているからこその問題です。 103 | 104 | 整形すると以下のようになります。 105 | 106 | ```php 107 | /** 108 | * @return array{ 109 | * 0: array{ID: int, Name: string, BirthDay: DateTimeImmutable}, 110 | * 1: array{ID: int, Name: string, BirthDay: DateTimeImmutable}, 111 | * 2: array{ID: int, Name: string, BirthDay: DateTimeImmutable}, 112 | * 3: array{ID: int, Name: string, BirthDay: DateTimeImmutable}, 113 | * } 114 | */ 115 | ``` 116 | 117 | これをコピペしてまたPHPDocに貼り付ける… でもいいのですが、コードにはこういうことが書いてあります 118 | 119 | ``` php 120 | // 仮実装なので仮データを返す 121 | ``` 122 | 123 | ということは、本実装では何個になるかというのは、あまり具体的な意味はなさそうです。 124 | 125 | さきほどのPHPDoc記法の使い分けを再掲しましょう。 126 | 127 | > * `array`, `array` (ジェネリック配列記法) 128 | > * **同じ構造が繰り返す場合に使う** 129 | > * `array{}`, `array{key: value}`, `array{int, string, string}` (array-shapes記法) 130 | > * 配列の中のキーごとに別の意味の値が格納される場合に使う 131 | 132 | 今回の場合はどの要素も引数は違いますが、同じ大きく見れば同じ構造のものの繰り返しと考えられます。 133 | 134 | つまり、このように書けます。 135 | 136 | ```php 137 | /** 138 | * @return array 139 | */ 140 | ``` 141 | この型は読みやすいように改行しても構いません。 142 | 143 | ```php 144 | /** 145 | * @return array 150 | */ 151 | ``` 152 | -------------------------------------------------------------------------------- /beginner/1.php: -------------------------------------------------------------------------------- 1 | $authors 16 | */ 17 | public function __construct( 18 | public string $title, 19 | public array $authors, 20 | ) {} 21 | } 22 | 23 | /** 24 | * @param non-empty-string $word 25 | * @param 'asc'|'desc' $order 26 | * @param positive-int $page 27 | * @return list 28 | */ 29 | function search(string $word, string $order, int $page): array 30 | { 31 | // 本来は検索エンジンからデータを取得する 32 | return match ($page) { 33 | 1 => [new Book('', [])], 34 | default => [], 35 | }; 36 | } 37 | 38 | $word = filter_var($_GET['word'] ?? ''); 39 | $order = filter_var($_GET['order'] ?? 'asc'); 40 | $page = filter_var($_GET['page'] ?? 1, FILTER_VALIDATE_INT); 41 | 42 | \PHPStan\dumpType(compact('word', 'order', 'page')); 43 | 44 | $books = search($word, $order, $page); 45 | 46 | \PHPStan\dumpType(compact('books')); 47 | -------------------------------------------------------------------------------- /beginner/4.php: -------------------------------------------------------------------------------- 1 | [!NOTE] 14 | > この節のコードは以下で確認できます 15 | > * **PHPStan Playground**: 16 | > * **File**: [`1.php`](./1.php) 17 | > * **CLI**: `./vendor/bin/phpstan analyze beginner/1.php` 18 | 19 | ```php 20 | $a = 'foo'; 21 | $b = 'bar'; 22 | $c = $a . $b; 23 | // . は文字列を結合する演算子 24 | 25 | \PHPStan\dumpType($a); 26 | \PHPStan\dumpType($b); 27 | \PHPStan\dumpType($c); 28 | ``` 29 | 30 | 複数の値をまとめてチェックしたいときは[`compact()`]で配列にまとめることでわかりやすくなることもあります。 31 | 32 | ```php 33 | $n = 5; 34 | $m = 2; 35 | $l = $n / $m; 36 | \PHPStan\dumpType(compact('n', 'm', 'l')); 37 | ``` 38 | 39 | 「これってPHPを実行した結果を画面に表示してるだけじゃないの?」と思われるかもしれません。禅問答のようですが、「***そうであって、そうではない***」のです。 40 | 41 | 何を言っているかわからないと思うので、次のようなコードを考えてみましょう。 42 | 43 | ```php 44 | $n = 5; 45 | if (rand() === 1) { 46 | $m = 2; 47 | } else { 48 | $m = 5; 49 | } 50 | 51 | $l = $n / $m; 52 | \PHPStan\dumpType(compact('n', 'm', 'l')); 53 | ``` 54 | 55 | `rand() === 1` という条件が成り立つ確率は大雑把に「**21億分の1**」です。PHPStanは`rand() === 1`という確率的な処理は**行なっていません**。どちらでも僅かにでも可能性があるならば、PHPStanは**どちらの可能性もある**と判断して`2|5`という型をつけます。さらに`$l = $n / $m`という式はどうでしょうか。`$n`には`5`という型がついていますが、`$m`は`2`と`5`の可能性があるので、`$l = 5 / 2` (= `2.5`) と `$l = 5 / 5` (= `1`) という2パターンが考えられます。ここでPHPStanは`$l`に`1|2.5`という型をつけます。これはPHPStanが行なう型付けの特殊な例などではなく、***PHPStanが常に行なっていること***です。 56 | 57 | > [!CAUTION] 58 | > `\PHPStan\dumpType()` は静的解析時に用いられる擬似的な関数ですが、実行時に定義されません。 59 | > 60 | > 実アプリケーションでは実行する前、あるいはユニットテスト実行前に取り除いてください。 61 | 62 | > [!TIP] 63 | > * PHPのコードは文末に`;`が必要です 64 | > * `.`演算子は文字列として結合します (`+`は常に数値の加算および配列マージを意味します) 65 | > * * `/` 演算子は数値の除算(割り算)を行います 66 | 67 | > [!IMPORTANT] 68 | > 🔜 **コードを好きに書き換えてみて、納得できたら次に進んでください** 69 | 70 | ## 2. 型宣言で関数に型をつける 71 | 72 | > [!NOTE] 73 | > この節のコードは以下で確認できます 74 | > * **PHPStan Playground**: 75 | > * **File**: [`2.php`](./2.php) 76 | > * **CLI**: `./vendor/bin/phpstan analyze beginner/2.php` 77 | 78 | PHPの関数に型を付けてみましょう。 79 | 80 | ```php 81 | function label($title) 82 | { 83 | return "label:{$title}"; 84 | } 85 | 86 | \PHPStan\Testing\assertType('string', label('foo')); 87 | ``` 88 | 89 | > [!TIP] 90 | > `\PHPStan\Testing\assertType(expected, actual)` は値が期待する型とPHPStanが認識している型の **文字列表現の一致** をチェックする関数です。`expected`と`actual`が同じ文字列なら何も出力されなくなります。 91 | > 92 | > ここでは使っていませんが、部分型関係を用いてチェックする `\PHPStan\Testing\assertSuperType(expected, actual)`もあります。 93 | 94 | > [!TIP] 95 | > 型宣言を含まない関数 `function f($arg1, $arg2) { ... }` は、どんな型の引数も受け入れ、どんな型の値を返すこともできます。 96 | > 97 | > * `function f(int $arg1, float $arg2) { ... }` 98 | > * `()`内に`type $arg`と書くことで、パラメータの型を宣言します 99 | > * `function f($arg1, $arg2): int|float { ... }` 100 | > * `()` の次に `: type` と書くことで、戻り値の型を宣言します 101 | > 102 | > パラメータと戻り値の型宣言は組み合わせることができます。 103 | 104 | PHPではパラメータ(仮引数リスト)や戻り値に型宣言を追加できます。関数やメソッドで型宣言された型は、実行時に**必ず保証**されます。 105 | 106 | 保証されるということは次のことを意味します: 107 | 108 | * パラメータで宣言された型は、実装内で必ず制約を満たします 109 | * ⇒ **制約を満たさない値が渡されることを心配する必要はありません** 110 | * 呼び出した結果、必ず宣言された型の制約を満たす戻り値が返されます 111 | * ⇒ **制約を満たさない値が返されることを心配する必要はありません** 112 | 113 | それぞれの箇所では、型宣言されたものが静的型付きであることが必ず保証されます。 114 | 115 | > [!TIP] 116 | > PHPの型宣言についてどのように振る舞うかチェックできます 117 | > * [php-playで確認する](https://php-play.dev/?c=DwfgDgFmAEAmCmBjANgQwE7wBQGcAu6AlongPp4CeY8OAvAIwCUA3AFCsBmArgHYmEB7HtDQAjeMlwFCPAObQAJHkJ5k8RgC5o%2BInNYBvVgEhMeLumEAiMRI36lKtQF9LbJ%2BwBuGUrC4BbMCwbSQByDgEBEMYWVi90H39A4KwmGKA&v=8.4&f=console) 118 | 119 | > [!IMPORTANT] 120 | > 🔜 **型を追加してエラーが出なくなったら次に進んでください** 121 | 122 | ## 3. 型を絞り込む 123 | 124 | > [!NOTE] 125 | > この節のコードは以下で確認できます 126 | > * **PHPStan Playground**: 127 | > * **File**: [`3.php`](./3.php) 128 | > * **CLI**: `./vendor/bin/phpstan analyze beginner/3.php` 129 | 130 | ユーザーがフォームから検索して、結果の書籍一覧を表示する画面を考えてみましょう。 131 | 132 | `search()`関数の実装は次のようになっています。 133 | 134 | ```php 135 | /** 136 | * @param non-empty-string $word 137 | * @param 'asc'|'desc' $order 138 | * @param positive-int $page 139 | * @return list 140 | */ 141 | function search(string $word, string $order, int $page): array 142 | { 143 | // 本来は検索エンジンからデータを取得する 144 | return match ($page) { 145 | 1 => [new Book('', [])], 146 | default => [], 147 | }; 148 | } 149 | ``` 150 | 151 | `/** … */`は**Doc comment**といい、関数やクラスの説明を記述できます。 `@param`や`@return`のような記述は**PHPDocタグ**と呼びます。`@param`はパラメータの詳細な型を、`@return`は関数・メソッドの戻り値の型を記述します。 152 | 153 | * `@param non-empty-string $word` 154 | * 空文字列での検索は不正なので、関数呼び出し側の責任でチェックする 155 | * `non-empty-string`とは、`''`(空文字列)以外の文字列のことです 156 | * `@param 'asc'|'desc' $order` 157 | * 検索結果を昇順と降順のどちらに並び変えるか 158 | * `@param positive-int $page` 159 | * 検索のページ数:最小値は`1` 160 | * `@return list` 161 | * `Book`クラスのリスト 162 | * [`list`型について](https://scrapbox.io/php/list%E5%9E%8B) 163 | 164 | > [!WARNING] 165 | > Doc commentは、必ず `/** ... */` (`*`が二つ!)から始まります。 166 | > 範囲コメントの `/* ... */` とは区別されるので十分に気をつけてください。 167 | > 168 | > エディタによってはPHPDocタグが色付けされるかによって区別できます。 169 | > ![Emacs PHP ModeでPHPDocタグが色付けされている画像](../pictures/highlighting-comments.png) 170 | 171 | 続いて、外部からの入力を値として取得します。 172 | 173 | ```php 174 | $word = filter_var($_GET['word'] ?? ''); 175 | $order = filter_var($_GET['order'] ?? 'asc'); 176 | $page = filter_var($_GET['page'] ?? 1, FILTER_VALIDATE_INT); 177 | 178 | $books = search($word, $order, $page); 179 | // 初期状態では以下のエラーが発生する 180 | // Parameter #1 $word of function search expects non-empty-string, string|false given. 181 | // Parameter #2 $order of function search expects 'asc'|'desc', string|false given. 182 | // Parameter #3 $page of function search expects int<1, max>, int|false given. 183 | ``` 184 | 185 | > [!TIP] 186 | > * [`filter_var()`](https://www.php.net/filter_var) 187 | > * 値をフィルタリングする関数です (名前に反して変数以外もフィルタできます) 188 | > * PHPStanは[検証フィルタ]とオプションによって型が変わります 189 | 190 | PHPStanは比較により**型を絞り込む**(type narrowing)ことができます。 191 | コードに以下のようなコードを追加して型を確認してみてください。 192 | 193 | ```php 194 | $word = filter_var($_GET['word'] ?? ''); 195 | \PHPStan\dumpType($word); // DumpedType: string|false 196 | 197 | if ($word === '' || $word === false) { 198 | \PHPStan\dumpType($word); // DumpedType: ''|false 199 | } else { 200 | \PHPStan\dumpType($word); // DumpedType: non-empty-string 201 | } 202 | 203 | \PHPStan\dumpType($word); // DumpedType: string|false 204 | ``` 205 | 206 | PHPStanは**制御フロー解析**を実装しており、`if`や`foreach`といった制御構造に従った変数スコープを保持しています。上記のコードでは初期状態で`string|false`だった変数が`if`と`else`でそれぞれ`''|false`と`non-empty-string`に分岐し、合流後は`string|false`に**戻っている**ことが確認できます。 207 | 208 | 型が絞り込まれた状態で制御フローを中断することで、その型を絞り込めます。中断とは、`return` `throw` `continue` `break` `exit` あるいは `never` 型の関数を読み込むなどです。 209 | 210 | ```php 211 | $word = filter_var($_GET['word'] ?? ''); 212 | \PHPStan\dumpType($word); // DumpedType: string|false 213 | 214 | if ($word === '' || $word === false) { 215 | throw new RangeException('$word を入力してください'); 216 | } else { 217 | \PHPStan\dumpType($word); // DumpedType: non-empty-string 218 | } 219 | 220 | \PHPStan\dumpType($word); // DumpedType: non-empty-string 221 | ``` 222 | 223 | これで型が絞り込まれた`else`の状態で固定できました。`else`のコードはまるごと削除しても構いません。さらに、型の絞り込みは**式の内部**でも起こります。 224 | 225 | ```php 226 | // strlen() に false を渡してしまう可能性があるのでエラー 227 | if (strlen($word) === 0 || $word === false) { 228 | // Parameter #1 $string of function strlen expects string, string|false given. 229 | throw new RangeException('$word を入力してください'); 230 | } 231 | ``` 232 | 233 | これは `||` の右辺と左辺を入れ替えることで解決します。 234 | 235 | ```php 236 | // false のときに左辺で処理が打ち切られるので strlen() の呼び出しを防げる 237 | if ($word === false || strlen($word) === 0) { 238 | ``` 239 | 240 | もっとも、このパターンは[`in_array()`](https://www.php.net/in_array)関数を用いて簡潔に絞り込めます。 241 | 242 | ```php 243 | if (in_array($word, [false, ''], true)) { 244 | throw new RangeException('$word を入力してください'); 245 | } 246 | ``` 247 | 248 | このように`in_array($var, ['foo', 'bar', 'buz'], true)`と書くことで、`$var === 'foo' || $var === 'bar' || $var === 'buz'`と等価になり、PHPStanも型の絞り込みを適切に認識します。 249 | 250 | 同じように、ほかの変数`$order`と`$page`の型も絞り込んでみてください。 251 | 252 | > [!IMPORTANT] 253 | > 🔜 **実装を修正してエラーが出なくなったら次に進んでください** 254 | > * `search()`を呼び出す際に渡す値の型を適切に絞り込みます 255 | > * `search()`の実装内部でエラーが出ないように適当な値を埋めてください 256 | 257 | ## 4. 型宣言で安全に型をつける 258 | 259 | 一方で、**ファイル単位**で `declare(strict_types=1);` の有無によって、スカラー型について「関数(メソッド)を呼び出す際の引数(argument, 実引数)」および「関数(メソッド)が返す戻り値の型」の振る舞いが変わります。 260 | 261 | * `strict_types=1`**あり** 262 | * 値と型が一致しないと`TypeError`を送出します 263 | * `strict_types=1`**なし** (または`0`) 264 | * [**関数のコンテクスト**][関数のコンテクスト]における型の相互変換を行い、文脈に沿わない値に`TypeError`を発生します 265 | 266 | 以下のようなコードを考えてみましょう。 267 | 268 | > [!NOTE] 269 | > この節のコードは以下で確認できます 270 | > * **PHPStan Playground**: 271 | > * **File**: [`4.php`](./4.php) 272 | > * **CLI**: `./vendor/bin/phpstan analyze beginner/4.php` 273 | 274 | ```php 275 | [!TIP] 330 | > 今回はとても大雑把に型をつけていますが、「数値文字列」ではなく「整数を表す文字列」だけをサポートしたい場合などはPHPStan 2.1時点ではPHPDocだけでは判定できず、PHPStan拡張を実装する必要があります。 331 | > この章ではこのような使い方ができるということだけを認識できれば目的達成です。 332 | 333 | > [!IMPORTANT] 334 | > 🔜 **実装と型宣言を修正してエラーが出なくなれば、この章は修了です🎉** 335 | 336 | ## 入門編の修了 337 | 338 | 🎉 修了おめでとうございます! 339 | 340 | ここまで学んだことを整理しましょう。 341 | 342 | * `\PHPStan\dumpType()`でPHPStanが認識している型を確認できる 343 | * `\PHPStan\Testing\assertType()`で期待する型との比較もできる 344 | * PHPの基本機能で関数・メソッドに型を付けることができる 345 | * PHPStanは制御フロー解析により型を絞り込める 346 | * PHPでは実行できるがPHPStanが受け付けないコードも存在することを認識できる 347 | * PHPDocタグでより詳細な型を付けることができる 348 | * `declare(strict_types=1)`の有無での振る舞いの差異がわかる 349 | * `filter_var()`を使った型の検査方法がわかる 350 | * 条件付き戻り値型の存在を認識している 351 | 352 | ここまでできれば、細かい理窟は抜きにして「PHPStanを使える」と言って差し支えないと思います。 353 | 354 | もちろん全ての機能を直ちに使いこなせるようになっている必要はありません。とはいえ、コードを書いて期待通りの型がついていない原因をチェックできるようになったといえるでしょう。 355 | 356 | より詳細なPHPStanの使い方を身に付けるために次のステップに進みましょう! 357 | 358 | [関数のコンテクスト]: https://www.php.net/manual/ja/language.types.type-juggling.php#language.types.type-juggling.function 359 | [`compact()`]: https://www.php.net/compact 360 | [検証フィルタ]: https://www.php.net/manual/ja/filter.filters.validate.php 361 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zonuexe/phpstan-typing-tutorial", 3 | "description": "Tutorial on typing with PHPStan", 4 | "license": "0BSD", 5 | "type": "project", 6 | "require-dev": { 7 | "phpstan/phpstan": "^2.1" 8 | }, 9 | "config": { 10 | "sort-packages": true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "7205af82ff0e8536ae7da4d72555a379", 8 | "packages": [], 9 | "packages-dev": [ 10 | { 11 | "name": "phpstan/phpstan", 12 | "version": "2.1.17", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/phpstan/phpstan.git", 16 | "reference": "89b5ef665716fa2a52ecd2633f21007a6a349053" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/phpstan/phpstan/zipball/89b5ef665716fa2a52ecd2633f21007a6a349053", 21 | "reference": "89b5ef665716fa2a52ecd2633f21007a6a349053", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "php": "^7.4|^8.0" 26 | }, 27 | "conflict": { 28 | "phpstan/phpstan-shim": "*" 29 | }, 30 | "bin": [ 31 | "phpstan", 32 | "phpstan.phar" 33 | ], 34 | "type": "library", 35 | "autoload": { 36 | "files": [ 37 | "bootstrap.php" 38 | ] 39 | }, 40 | "notification-url": "https://packagist.org/downloads/", 41 | "license": [ 42 | "MIT" 43 | ], 44 | "description": "PHPStan - PHP Static Analysis Tool", 45 | "keywords": [ 46 | "dev", 47 | "static analysis" 48 | ], 49 | "support": { 50 | "docs": "https://phpstan.org/user-guide/getting-started", 51 | "forum": "https://github.com/phpstan/phpstan/discussions", 52 | "issues": "https://github.com/phpstan/phpstan/issues", 53 | "security": "https://github.com/phpstan/phpstan/security/policy", 54 | "source": "https://github.com/phpstan/phpstan-src" 55 | }, 56 | "funding": [ 57 | { 58 | "url": "https://github.com/ondrejmirtes", 59 | "type": "github" 60 | }, 61 | { 62 | "url": "https://github.com/phpstan", 63 | "type": "github" 64 | } 65 | ], 66 | "time": "2025-05-21T20:55:28+00:00" 67 | } 68 | ], 69 | "aliases": [], 70 | "minimum-stability": "stable", 71 | "stability-flags": {}, 72 | "prefer-stable": false, 73 | "prefer-lowest": false, 74 | "platform": {}, 75 | "platform-dev": {}, 76 | "plugin-api-version": "2.6.0" 77 | } 78 | -------------------------------------------------------------------------------- /phpstan.dist.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: max 3 | phpVersion: 80401 4 | paths: 5 | - beginner/ 6 | - basic/ 7 | tmpDir: . 8 | includes: 9 | - vendor/phpstan/phpstan/conf/bleedingEdge.neon 10 | -------------------------------------------------------------------------------- /pictures/highlighting-comments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonuexe/phpstan-typing-tutorial/dcb5b5f538f896129026c13de3a74e020bfd693f/pictures/highlighting-comments.png -------------------------------------------------------------------------------- /pictures/splited-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonuexe/phpstan-typing-tutorial/dcb5b5f538f896129026c13de3a74e020bfd693f/pictures/splited-screen.png --------------------------------------------------------------------------------