Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2485194
PhpstanLinter.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
6 KB
Referenced Files
None
Subscribers
None
PhpstanLinter.php
View Options
<?php
/**
* @copyright Copyright 2017-present Appsinet. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/** Uses phpstan to lint php files */
final
class
PhpstanLinter
extends
ArcanistExternalLinter
{
/**
* @var string Config file path
*/
private
$configFile
=
null
;
/**
* @var string Rule level
*/
private
$level
=
null
;
/**
* @var string Autoload file path
*/
private
$autoloadFile
=
null
;
public
function
getInfoName
()
{
return
'phpstan'
;
}
public
function
getInfoURI
()
{
return
''
;
}
public
function
getInfoDescription
()
{
return
pht
(
'Use phpstan for processing specified files.'
);
}
public
function
getLinterConfigurationName
()
{
return
'phpstan'
;
}
public
function
getDefaultBinary
()
{
return
'phpstan'
;
}
public
function
getInstallInstructions
()
{
return
pht
(
'Install phpstan following the official guide at https://github.com/phpstan/phpstan#installation'
);
}
public
function
shouldExpectCommandErrors
()
{
return
true
;
}
public
function
getVersion
()
{
list
(
$stdout
)
=
execx
(
'%C --version'
,
$this
->
getExecutableCommand
());
$matches
=
array
();
$regex
=
'/(?P<version>
\d
+
\.\d
+
\.\d
+)/'
;
if
(
preg_match
(
$regex
,
$stdout
,
$matches
))
{
return
$matches
[
'version'
];
}
else
{
return
false
;
}
}
protected
function
getMandatoryFlags
()
{
$flags
=
array
(
'analyse'
,
'--no-progress'
,
'--error-format=checkstyle'
,
'--memory-limit=1G'
);
if
(
null
!==
$this
->
configFile
)
{
array_push
(
$flags
,
'-c'
,
$this
->
configFile
);
}
if
(
null
!==
$this
->
level
)
{
array_push
(
$flags
,
'-l'
,
$this
->
level
);
}
if
(
null
!==
$this
->
autoloadFile
)
{
array_push
(
$flags
,
'-a'
,
$this
->
autoloadFile
);
}
return
$flags
;
}
public
function
getLinterConfigurationOptions
()
{
$options
=
array
(
'config'
=>
array
(
'type'
=>
'optional string'
,
'help'
=>
pht
(
'The path to your phpstan.neon file. Will be provided as -c <path> to phpstan.'
),
),
'level'
=>
array
(
'type'
=>
'optional string'
,
'help'
=>
pht
(
'Rule level used (0 loosest - max strictest). Will be provided as -l <level> to phpstan.'
),
),
'autoload'
=>
array
(
'type'
=>
'optional string'
,
'help'
=>
pht
(
'The path to the auto load file. Will be provided as -a <autoload_file> to phpstan.'
),
),
);
return
$options
+
parent
::
getLinterConfigurationOptions
();
}
public
function
setLinterConfigurationValue
(
$key
,
$value
)
{
switch
(
$key
)
{
case
'config'
:
$this
->
configFile
=
$value
;
return
;
case
'level'
:
$this
->
level
=
$value
;
return
;
case
'autoload'
:
$this
->
autoloadFile
=
$value
;
return
;
default
:
parent
::
setLinterConfigurationValue
(
$key
,
$value
);
return
;
}
}
protected
function
getDefaultMessageSeverity
(
$code
)
{
return
ArcanistLintSeverity
::
SEVERITY_WARNING
;
}
protected
function
parseLinterOutput
(
$path
,
$err
,
$stdout
,
$stderr
)
{
$result
=
array
();
if
(!
empty
(
$stdout
))
{
$stdout
=
substr
(
$stdout
,
strpos
(
$stdout
,
'<?xml'
));
$checkstyleOutpout
=
new
SimpleXMLElement
(
$stdout
);
$errors
=
$checkstyleOutpout
->
xpath
(
'//file/error'
);
foreach
(
$errors
as
$error
)
{
$violation
=
$this
->
parseViolation
(
$error
);
$violation
[
'path'
]
=
$path
;
$result
[]
=
ArcanistLintMessage
::
newFromDictionary
(
$violation
);
}
}
return
$result
;
}
/**
* Checkstyle returns output of the form
*
* <checkstyle>
* <file name="${sPath}">
* <error line="12" column="10" severity="${sSeverity}" message="${sMessage}" source="${sSource}">
* ...
* </file>
* </checkstyle>
*
* Of this, we need to extract
* - Line
* - Column
* - Severity
* - Message
* - Source (name)
*
* @param SimpleXMLElement $violation The XML Entity containing the issue
*
* @return array of the form
* [
* 'line' => {int},
* 'column' => {int},
* 'severity' => {string},
* 'message' => {string}
* ]
*/
private
function
parseViolation
(
SimpleXMLElement
$violation
)
{
return
array
(
'code'
=>
$this
->
getLinterName
(),
'name'
=>
(
string
)
$violation
[
'message'
],
'line'
=>
(
int
)
$violation
[
'line'
],
'char'
=>
(
int
)
$violation
[
'column'
],
'severity'
=>
$this
->
getMatchSeverity
((
string
)
$violation
[
'severity'
]),
'description'
=>
(
string
)
$violation
[
'message'
]
);
}
/**
* @return string Linter name
*/
public
function
getLinterName
()
{
return
'phpstan'
;
}
/**
* Map the regex matching groups to a message severity. We look for either
* a nonempty severity name group like 'error', or a group called 'severity'
* with a valid name.
*
* @param string $severity_name dict Captured groups from regex.
*
* @return string @{class:ArcanistLintSeverity} constant.
*
* @task parse
*/
private
function
getMatchSeverity
(
$severity_name
)
{
$map
=
array
(
'error'
=>
ArcanistLintSeverity
::
SEVERITY_ERROR
,
'warning'
=>
ArcanistLintSeverity
::
SEVERITY_WARNING
,
'info'
=>
ArcanistLintSeverity
::
SEVERITY_ADVICE
,
);
foreach
(
$map
as
$name
=>
$severity
)
{
if
(
$severity_name
==
$name
)
{
return
$severity
;
}
}
return
ArcanistLintSeverity
::
SEVERITY_ERROR
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Fri, Nov 21, 7:11 AM (1 d, 13 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
387410
Default Alt Text
PhpstanLinter.php (6 KB)
Attached To
Mode
R2 kolab
Attached
Detach File
Event Timeline
Log In to Comment