PHPStanで始める継続的静的解析 #phperkaigi /php-static-analysis

PHPStanで始める継続的静的解析 #phperkaigi /php-static-analysis

PHPerKaigi 2018のトーク内容です。

2295fec4d38f59366f983d38628e6ca0?s=128

Hiraku NAKANO

March 10, 2018
Tweet

Transcript

  1. PHPStanʹΑΔ ܧଓత੩తղੳ @hiraku

  2. ࣗݾ঺հ • த໺ ୓ (@Hiraku) • https://github.com/hirak • גࣜձࣾϝϧΧϦ →

    ϝϧϖΠ • PHPྺ10೥͙Β͍ • Composerؔ࿈ͷൃදΛΑ͍ͯ͘͠·͢
  3. 2݄͔ΒϝϧϖΠࣾʹҟಈ͠·ͨ͠ • ۚ༥ؔ࿈ͷ৽نࣄۀΛߦ͏
 ϝϧΧϦͷάϧʔϓձࣾ • શํ໘ɺϝϯόʔืूதʂ

  4. ʲએ఻ऴΘΓʳ

  5. ΋͘͡ • dynamic ͱ static • PHPStanͷ঺հ ~ϝϧΧϦͰͷ׆༻ࣄྫ • staticͱͷڑ཭ײ

    • ࣮ફɾܕ஫ऍ
  6. ୈ1෦ dynamic ͱ static PHPStanʹΑΔܧଓత੩తղੳ

  7. ྑ͍ίʔυ͔Ͳ͏͔ ΛݟۃΊΔ2ͭͷΞϓϩʔν dynamic / static

  8. dynamic = ಈ͔ͯ͠ΈΔ • ςετ (ࣗಈ/खಈͲͪΒͰ΋) • ୅ද஋Λ༩࣮͑ͯߦˠ૝ఆ௨Γ͔൱͔ • ࢓༷ͷ֬ೝʹ΋࢖͑Δ

  9. dynamicͷ೉఺ • ຊ౰ʹ࣮ߦ͞ΕΔͱࠔΔ৔߹͕͋Δ
 (ϛαΠϧൃࣹͱ͔) • શ෦ςετ͢Δͷ͸ແཧ
 (64bit intͷҾ਺㲈1844ژύλʔϯ) • ϞοΫΛ༻ҙͯ͠ɺ୅ද஋ΛબΜͰ…


    ޻෉͕ඞཁ
  10. static = ಈ͔ͣ͞ʹಡΉ • ίʔυϨϏϡʔ / ੩తݕࠪ • ಈ͔͞ͳ͍ͷͰԿ΋ى͖ͳͯ҆͘৺ •

    ςετίʔυ͕ͳͯ͘΋ݕࠪՄೳ
  11. staticͷ೉఺ • ϓϩάϥϛϯάݴޠ, ίʔυͷελΠϧʹ
 ΑͬͯɺͰ͖ΔϨϕϧ͕มΘΔ • ཧ࿦্ɺܾఆͰ͖ͳ͍छྨͷ໰୊͕͋Δ • ແݶϧʔϓ/ఀࢭੑͳͲ •

    (ϥΠεͷఆཧͱ͔Ͱάάͬͯʂ)
  12. ͳͥ͜Μͳ࿩Λ ͍ͯ͠Δͷ͔

  13. ϝϧΧϦͷ PHPίʔυͷ࿩

  14. ~2016೥·Ͱ •!"# ͷશαʔόʔαΠυϩδοΫ͕
 1ͭͷrepositoryͩͬͨ • ifͰ෼ذଟ਺ • ΤϯδχΞϦιʔεͷڞ༗Խ

  15. 2017೥ʹى͖ͨ͜ͱ •!ͱ"# ͷίʔυ͕෼཭ͨ͠ • ݕূൣғΛڱͯ͘͠։ൃεϐʔυUP • ࢖ͬͯͳ͍ίʔυͷ࡟আ͕ඞཁʹͳͬͨ

  16. େྔͷίʔυ࡟আΛࢼΈΔ

  17. Өڹൣғͷେ͖ͳमਖ਼

  18. യવͱͨ͠ෆ҆

  19. ൃੜͨ͠໰୊ • ϚχϡΞϧͰQA͢Δͱ͔͔࣌ؒΔ • ϦϑΝΫλϦϯά୲౰ऀ͕ϢχοτςετΛ ॻ͘ͱ͜Ζ͔Β࢝Ίͳ͍ͱ͍͚ͳ͍ • ϦϦʔεʹ༐ؾ͕ඞཁ

  20. ༐ؾ͕΄͍͠ʂ

  21. ྑ͍ίʔυ͔Ͳ͏͔ ΛݟۃΊΔ2ͭͷΞϓϩʔν dynamic / static

  22. staticํ໘͔Β΋߈Ί͍ͨ • ςετΛ૿΍͞ͳͯ͘΋࣮ߦͰ͖Δ • मਖ਼ྔ͕ଟͯ͘΋ػցͳΒશ෦ϨϏϡʔͯ͘͠ΕΔ • Ͳ͏ͤͳΒCIαʔόʔʹ૊ΈࠐΜͰɺக໋తͳ΋ͷ ͸CIΛࣦഊ͍ͤͨ͞ • PHPStorm͸༏ल͚ͩͲɺCIʹ૊ΈࠐΉͷ͕


    ೉͍͠
  23. ୈ2෦ PHPStanͷ঺հ PHPStanʹΑΔܧଓత੩తղੳ

  24. PHPStanͱ͸ • https://github.com/phpstan/phpstan • PHP Static Analysis Tool ͷུ •

    ಈ࡞ʹ͸PHP 7.1Ҏ্͕ඞཁ • PHP5ͷίʔυ΋ղੳͯ͘͠ΕΔ • nikic/php-parserϕʔεͰͷղੳΛߦ͏ (ͷͰׂͱݡ͍)
  25. Phanͱͷҧ͍ • https://github.com/phan/phan • ͲͪΒ΋ࣅͨ͜ͱ͕Մೳ • ͪΐͬͱઃܭࢥ૝͕ҧ͏(͋ͱͰղઆ͠·͢)

  26. ※ϝϧΧϦͰPhanΛ
 ࠾༻͠ͳ͔ͬͨཧ༝ • PHPDocΛख͕͔Γʹ͢ΔͷͰɺPHPDoc Λ ͔ͬ͠Γॻ͍ͯͳ͍ͱπϥ͔ͬͨ • ext-ast Λඞཁͱ͢ΔͷͰऔΓճ͕͠ѱ͔ͬͨ •

    ʮͱΓ͋͑ͣɺ͜͜Ͱൃੜ͍ͯ͠Δ͜ͷΤϥʔ ͸ແࢹ͓ͯ͜͠͏ʯ͍ͬͯ͏ઃఆͷॊೈ͞
  27. ݕग़ͯ͘͠ΕΔ໰୊ͷྫ • ม਺ͷະఆٛ • nullͷՄೳੑ͕͋ΔͷʹϝιουݺΜͰΔ • PHPDocͷߏจෆඋ • ܕͷෆඋશൠ

  28. None
  29. div() should return int but returns float. floatͳΒOK

  30. Install • composer require --dev phpstan/phpstan • phar൛΋͋Δ͠ɺglobal requireͯ͠΋୯ಠಈ ࡞͢Δ

    • docker hubʹ΋Πϝʔδ͕͋Δ • https://hub.docker.com/r/phpstan/phpstan/
  31. ※όʔδϣϯҧ͍ʹ஫ҙ • 2018೥3݄࣌఺ͷstable (0.9ܥ) ͱɺdev (1.0ܥ)͸ ͔ͳΓΦϓγϣϯʹҧ͍͕͋Γ·͢ • githubͷREADME.mdΛಡΉͱ͖͸஫ҙ •

    ͜͜Ͱ͸dev൛(1.0ܥ)Λϕʔεʹ࿩͠·͢ • composer global require "phpstan/phpstan:@dev" ͳͲͰೖΓ·͢
  32. Run • ϓϩδΣΫτϩʔΧϧʹೖͬͯΕ͹
 $ vendor/bin/phpstan analyseͰىಈ • ղੳ͍ͨ͠σΟϨΫτϦʹͯɺphpstanίϚϯ υΛىಈ͢Ε͹ྑ͍

  33. ࣮ߦͨ͠ͱ͖ͷΠϝʔδ $ phpstan analyse Note: Using configuration file /Users/hiraku/src/github.com/hirak/ prestissimo/phpstan.neon.dist.

    12/12 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100% ------ ------------------------------------------------------------------- Line src/BaseRequest.php ------ ------------------------------------------------------------------- 23 PHPDoc tag @var has invalid value ([string => string]): Unexpected token "[", expected TOKEN_IDENTIFIER at offset 9 120 PHPDoc tag @param has invalid value ($io): Unexpected token "$io", expected TOKEN_IDENTIFIER at offset 18 120 PHPDoc tag @param has invalid value ($githubDomains): Unexpected token "$githubDomains", expected TOKEN_IDENTIFIER at offset 70 120 PHPDoc tag @param has invalid value ($gitlabDomains): Unexpected token "$gitlabDomains", expected TOKEN_IDENTIFIER at offset 99 ------ -------------------------------------------------------------------
  34. Configure • ઃఆϑΝΠϧ͕͋Ε͹ɺ phpstan analyse ͩ ͚ͰղੳͰ͖Δ(1.0ܥ) • .neonͱ͍͏ɺYAMLͷΑ͏ͳઃఆϑΝΠϧΛ࢖͏ •

    stable൛(0.9ܥ)ͱɺdev൛(1.0ܥ)Ͱ͔ͳΓมߋ͕ ೖ͍ͬͯΔɻdev൛ͷํ͕ศར͞ˢˢ
  35. 1.0ܥͷphpstan.neon.distྫ • phpstan.neon→~.distͷ ॱʹ୳ͯ͠ಡΉ • .neon͸.gitignore͠ͱ͘ ͱڞಉ։ൃऀ͕޷͖ʹ֦ ுͰ͖Δ

  36. CIͱͷ࿈ܞ • Τϥʔ͕͋ΔͱίϚϯυͷऴྃεςʔλε͕1ʹͳΔͷ Ͱɺࣦഊ൑ఆ͸༰қ • ϝϧΧϦͰ͸CircleCIΛ࢖ͬͯ2ஈ֊Ͱ࿈ܞ • ࠷௿ݶͷϧʔϧ͸CIࣗମΛམͱ͢ • ݫ͠Ίͷϧʔϧ͸reviewdogͰPull

    Requestʹίϝϯ τͤ͞Δ(CI͸མͱ͞ͳ͍)
  37. reviewdogศར • http://haya14busa.com/reviewdog/

  38. ϧʔϧΛΏΔΊΔํ๏ • levelΦϓγϣϯͰࡶʹࢦఆ • 0 ~ 7ɺmaxͱ͍͏ΤΠϦΞε΋͋Δ • https://github.com/phpstan/phpstan/tree/ master/conf

  39. ஸೡʹϧʔϧΛΧελϜ • level͸ࢦఆͤͣʹ parameters
 .customRulesetUsed Λtrueʹ • rulesʹฒ΂Δ

  40. ࡶʹ཈੍ • parameters
 .ignoreErrors
 Λ௥Ճ • ਖ਼نදݱͰΤϥʔ ϝο ηʔδΛϚον͞ ͤͯແࢹͰ͖Δ(!)

    ͱ͔ Λ௥Ճͨ͠Β શΤϥʔ཈ࢭʂ
  41. ୈ3෦ staticͱͷڑ཭ײ PHPStanʹΑΔܧଓత੩తղੳ

  42. None
  43. autoloadͷઃఆ͕͋Δ

  44. autoloadͱ͸ • ಛఆͷؔ਺Λ࣮ߦͯ͠ϑΝΠϧΛ require_once͠ɺΫϥεΛϩʔυ͢Δ • ࠷ۙͷPHPϓϩδΣΫτͩͱී௨ʹ࢖ΘΕͯ ͍Δ

  45. ศར͚ͩͲ…ʁ

  46. autoloadͱ͸ • ಛఆͷؔ਺Λ࣮ߦͯ͠ϑΝΠϧΛ require_once͠ɺΫϥεΛϩʔυ͢Δ • ࠷ۙͷPHPϓϩδΣΫτͩͱී௨ʹ࢖ΘΕͯ ͍Δ

  47. ίʔυΛ࣮ߦ͠ͳ͍ͱݴͬͨͳɻ ͋Ε͸ӕͩɻ • PHPStan͸Ұ෦ίʔυΛ࣮ߦ͢Δ • "੩త"ͪΌ͏Μ͔ʔ͍ • Phan͸autoload͕ઃఆͰ͖ͳ͍͕ɺ੩తղੳ Ͱ͋Δ͜ͱʹ஧࣮

  48. "࣮ߦ"͢ΔͷͰ • autoloadϑΝΠϧࣗମ͕exit͢Δͱphpstan΋ Ϋϥογϡ͢Δ • autoloadʹΑͬͯݺͼग़͞ΕͨϑΝΠϧ͸
 ࣮ߦ͞Εͯ͠·͏

  49. PHPͷ੩తղੳͮ͠Β͍ͱ͜Ζ • ίϯύΠϧͱϥϯλΠϜͷڥք͕͸͖ͬΓ͠ͳ͍ ఆٛ ࣮ߦ ఆٛ ࣮ߦ ఆٛ ࣮ߦ ఆٛ

    ࣮ߦ autoload autoload autoload
  50. ৘੎ͷมԽ

  51. "ϞμϯͳελΠϧ"ͷཱ֬ • autoload͕ී௨ɻcomposerͷඪ४ʹै͏ • ن໿ͱͯ͠PSR-1Λक͍ͬͯΔ͔ΒɺΫϥεఆ ٛʹ͸ఆٛҎ֎ͷ෭࡞༻͕ͳ͍͸ͣ

  52. PHPStanͷཱͪҐஔ • Ϋϥεϩʔυ͚ͩϥϯλΠϜΛڐ͢ • "ϞμϯͳελΠϧʹ࠷దԽ" ఆٛ ࣮ ߦ autoload ఆٛ

    ࣮ ߦ ఆٛ ఆٛ autoload autoload
  53. ಘΒΕͨ΋ͷ • ಋೖͷ͠қ͞ • ղੳͷߴ଎ੑ • গ͠ͷةݥੑ

  54. ݸਓతҙݟ • 100%੩తʹؤுΔͷͰ͸ͳ͘ɺಈత΋ࠞͥΔ • PHPΒͯ͘͠ɺ͍͍མͱ͠ॴͷπʔϧ • ܕએݴͷه๏ͷॆ࣮΍ɺPSR-1ͳͲͷจԽ͕Ͱ ͖͔ͨΒͦ͜ɺ࡞Εͨπʔϧ

  55. ୈ4෦ ࣮ફɾܕ஫ऍ PHPStanʹΑΔܧଓత੩తղੳ

  56. PHPStanͷجຊಈ࡞ • ίʔυʹॻ͍ͯ͋Δܕ஫ऍΛ΋ͱʹɺ͋Γ͑ ͳ͍ૢ࡞Λ͍ͯͨ͠ΒΤϥʔʹ͢Δ • ܕ஫ऍ͕ஸೡͩͱɺݕࠪ΋ஸೡʹͳΔ • ܕʹର͢Δ஌͕ࣝ͋Δͱ͸͔ͲΔ

  57. ܕએݴͱPHPDoc • PHPͷܕ஫ऍ͸ࣗ༝౓͕௿͍ • ͳͷͰɺPHPDocΛࠞͥͯ࢖͏ • PHPStan͸ͲͪΒ΋ղऍͯ͘͠ΕΔ

  58. PHP7ͷܕએݴ • int, float, bool, string, array, Ϋϥε/Πϯλʔϑ Σʔε໊, callable,

    iterable • ?Λઌ಄ʹ͚ͭΔ/σϑΥϧτҾ਺Λnullʹ͢Δ ͱnullable
  59. εΧϥܕʹ஫ҙ • double͡Όͳͯ͘float • integer, boolean͡Όͳͯ͘int, bool • ؒҧ͑ΔͱΫϥε໊ͱͯ͠ղऍ͞ΕΔ

  60. PHPDoc • PHPެࣜͷػೳͰ͸ͳ͍ ͕ɺ΄΅ඪ४͕͋Δ • docBlock (/** ~ */) Λؔ਺

    ͷ௚લʹهೖ
  61. PHPDocͷߏจ • @param ܕ໊ $ม਺໊ આ໌ • εϖʔε۠੾ΓͳͷͰɺܕ໊ʹεϖʔεΛೖΕ ͯ͸͍͚ͳ͍ •

    ܕ͸লུෆՄ • JavaDocͷͭ΋ΓͰॻ͘ͱจ๏Τϥʔ
  62. PHPDocಠࣗͷܕه๏ • \DateTime[] ͳͲͷܕݻఆ഑ྻ • string|null ͳͲͷunion෩ه๏ • true/false ΋ॻ͚Δ

    • @return $this • ϝιουνΣʔϯͷ໌ࣔ
  63. PHPStanͰunionͷݕࠪ →hoge() should return string but returns bool|string.

  64. PHPStanͰunionͷݕࠪ →hoge() should return string but returns false|string.

  65. PHPStanͰunionͷݕࠪ ݁ߏ͔͍͜͠ʂ

  66. PHPStanͰnullableͷݕࠪ • ?string ΍ string|null ͲͪΒ΋ೝࣝ͢Δ • ifจͰݕ͔ࠪͯ͠Βࢀর͢Ε͹ݕࠪOK

  67. ݱঢ়Ͱ͖ͳ͍͜ͱ • ܧঝؔ܎Λ͏·͘ѻ͑ͳ͍͜ͱ͕͋Δ
 (൓มɾڞม) • هड़Ͱ͖ͳ͍ܕ͕͍ͬͺ͍ • ࿈૝഑ྻͷৄ͍͠هड़

  68. Ϧείϑͷஔ׵ݪଇ(LSP) • ܕΛ೿ੜͨ͠ͱ͖ɺ
 ΋ͱͷೳྗ͕ݮͬͨΒμϝ • ຬͨͯ͠Ε͹ϝιουΛ
 ্ॻ͖ͯ͠΋OK جఈ

  69. LSPతʹOKͳ্ॻ͖ • ϝιουΛΦʔόʔϥΠυͨ͠ͱ͖ɺPHPެ ࣜͰ͸ܕએݴΛมߋͰ͖ͳ͍ • 7.2Ͱܕফڈ͚ͩڐՄ͞Εͨ • PHPDoc + PHPStanͩͱνΣοΫ͞Εͳ͍

  70. هड़ํ๏͕ͳ͍ܕͷྫ • ArrayObject<int> • function (int): int • [int, float]

    (࿈૝഑ྻͰ)
  71. ΫϥεͰؤுΕͳ͘΋ͳ͍ Ҏ߱͸IntArrayͰܕΛهड़ ࣮૷ʹ΋IntArrayΛ࢖͏

  72. ΫϥεͰؤுΕͳ͘΋ͳ͍ callableͰ͸ͳ͘ ΠϯλʔϑΣʔε ΫϩʔδϟͰ͸ͳ͘ ແ໊Ϋϥε

  73. ʘ໘౗͍͘͞ʗ

  74. ʘHackͳΒͰ͖Δͷʹʂʗ

  75. ແཧʹؤுΔඞཁ͸ͳ͍

  76. ςετ͢Ε͹͍͍ΜͰ͢ • PHP͸ಈతܕ෇͖ݴޠ • ࠔͬͨΒܕ஫ऍΛফͯ͠mixedʹ໭Ζ͏ • ෆ҆ͳΒϢχοτςετ

  77. ·ͱΊ • PHP΋੩తղੳ͕͠΍͘͢ͳͬͨ • staticํ໘Λॆ࣮ͤ͞Ε͹ɺയવͱͨ͠ෆ͕҆ ݮΒͤΔ • ಈతͱ੩తͷΦΠγΠͱ͜ΖΛ࢖͍ͬͯ͜͏

  78. ࢀߟจݙ • https://ja.wikipedia.org/wiki/ແݶϧʔϓ • PHPDocͷߏจ͸PSR-5͕ಡΈ΍͍͢
 https://github.com/php-fig/fig-standards/blob/ master/proposed/phpdoc.md • https://ja.wikipedia.org/wiki/ڞมੑͱ൓มੑ _(ܭࢉػՊֶ)