Larastan es una herramienta por línea de comandos de análisis para aplicar a tus proyectos Laravel y que está basada en PHPStan. PHPStan es una herramienta de análisis de código estático que permite analizar el código PHP, está herramienta te permitirá encontrar bugs sin escribir tests.
Desde hace tiempo llevo buscando una herramienta que analice el código desarrollado de forma estática y que te ayude a subsanar bugs no visibles, o alguna mala práctica de código de la que no eres consciente, nadie es perfecto, es por ello que inicialmente hice pruebas con Sonarqube sobre todo porque me ofrecía la posibilidad de usarlo en varios stacks tecnológicos como podrían ser PHP o Java; pero buscaba algo más sencillo y dentro del entorno del propio proyecto Laravel pero finalmente me decidí por una herramienta ya conocida como PHPStan y buscar la manera de aplicarlo fácilmente en Laravel, esto lo podemos hacer con Larastan, como comentaba PHPStan una herramienta de análisis de código por línea de comandos con varios niveles de exigencia.
PHPStan es una herramienta de análisis estático que permite analizar el código PHP
https://phpstan.org/
Índice
- Instalación y funcionamiento
- Analizamos el código de ADMIN-APP
- Corregimos errores y mejoramos el código
- Error 1: should return Illuminate\Http\Response but returns Illuminate\Http\JsonResponse
- Error 2: Property App\Models\CmCustomer::property does not accept mixed
- Error 3: 17 Method App\Http\Controllers\CmCustomerController::index() should return Illuminate\Http\Response but return statement is missing
- Error 4: Access to an undefined property App\Http\Resources\CmCustomerResource::$property
- Error 5: Expression in empty() is not falsy
- Error 6: corregimos varios errores en la documentación de métodos
- Error 7: Corregimos errores de programación en app/Http/Controllers/API/CmCustomerController.php
- Error 8: Corregimos errores de programación en app/Http/Controllers/API/CmEnterpriseController.php
- Error 9: Corregimos errores de tipado en app/Jobs/CmCustomerCreated.php y en app/Jobs/CmCustomerUpdated.php
- Error 10: Property is never read, only written en app/Jobs/CmCustomerCreated.php y app/Jobs/CmCustomerUpdated.php
1. Instalación y funcionamiento
2. Analizamos el código de ADMIN-APP
Después de lanzar los errores obtenemos la siguiente información por línea de comandos 86 errores en el nivel 9, el nivel más alto y que es el que suelo aplicar en mis proyectos, en este código de ejemplo lo he ido desarrollando sin pasar la herramienta de PHPStan para tener un pull de errores sobre el que ir desgranando y entendiendo el análisis que nos da PHPStan y cómo actualizarlo:
$ ./vendor/bin/phpstan analyse
Note: Using configuration file /home/xules/xulprocx-local/DOCKER-Projects/Docker-Laravel/cx-laravel-microservices-customerdb-publish/cx-lpm-customerdb-admin/cx-lpm-customerdb-admin-app/phpstan.neon.
28/28 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
------ --------------------------------------------------------------------------------------------------------------------------------------------------
Line Http/Controllers/API/CmCustomerController.php
------ --------------------------------------------------------------------------------------------------------------------------------------------------
34 Method App\Http\Controllers\API\CmCustomerController::index() should return Illuminate\Http\Response but returns Illuminate\Http\JsonResponse.
58 Expression in empty() is not falsy.
61 Method App\Http\Controllers\API\CmCustomerController::store() should return Illuminate\Http\Response but returns Illuminate\Http\JsonResponse.
69 Method App\Http\Controllers\API\CmCustomerController::store() should return Illuminate\Http\Response but returns Illuminate\Http\JsonResponse.
85 Method App\Http\Controllers\API\CmCustomerController::store() should return Illuminate\Http\Response but returns Illuminate\Http\JsonResponse.
98 Method App\Http\Controllers\API\CmCustomerController::show() should return Illuminate\Http\Response but returns Illuminate\Http\JsonResponse.
111 Method App\Http\Controllers\API\CmCustomerController::show() should return Illuminate\Http\Response but returns Illuminate\Http\JsonResponse.
125 Method App\Http\Controllers\API\CmCustomerController::update() should return Illuminate\Http\Response but returns Illuminate\Http\JsonResponse.
145 Expression in empty() is not falsy.
148 Method App\Http\Controllers\API\CmCustomerController::update() should return Illuminate\Http\Response but returns Illuminate\Http\JsonResponse.
150 Property App\Models\CmCustomer::$identerprise (int) does not accept mixed.
151 Property App\Models\CmCustomer::$customer (string) does not accept mixed.
152 Property App\Models\CmCustomer::$contact (string) does not accept mixed.
153 Property App\Models\CmCustomer::$customerstate (string) does not accept mixed.
154 Property App\Models\CmCustomer::$paymentmethod (string) does not accept mixed.
155 Property App\Models\CmCustomer::$elanguage (string) does not accept mixed.
156 Property App\Models\CmCustomer::$country (string) does not accept mixed.
157 Property App\Models\CmCustomer::$currency (string) does not accept mixed.
158 Property App\Models\CmCustomer::$address (string) does not accept mixed.
171 Method App\Http\Controllers\API\CmCustomerController::update() should return Illuminate\Http\Response but returns Illuminate\Http\JsonResponse.
180 Method App\Http\Controllers\API\CmCustomerController::destroy() has parameter $idcustomer with no type specified.
180 PHPDoc tag @param references unknown parameter: $id
183 Cannot access property $customer on App\Models\CmCustomer|Illuminate\Database\Eloquent\Collection<App\Models\CmCustomer>|null.
185 Cannot call method delete() on App\Models\CmCustomer|Illuminate\Database\Eloquent\Collection<App\Models\CmCustomer>|null.
191 Method App\Http\Controllers\API\CmCustomerController::destroy() should return Illuminate\Http\Response but returns Illuminate\Http\JsonResponse.
------ --------------------------------------------------------------------------------------------------------------------------------------------------
------ ----------------------------------------------------------------------------------------------------------------------------------------------------
Line Http/Controllers/API/CmEnterpriseController.php
------ ----------------------------------------------------------------------------------------------------------------------------------------------------
63 Method App\Http\Controllers\API\CmEnterpriseController::index() should return Illuminate\Http\Response but returns Illuminate\Http\JsonResponse.
86 Method App\Http\Controllers\API\CmEnterpriseController::store() has no return type specified.
99 Expression in empty() is not falsy.
149 Method App\Http\Controllers\API\CmEnterpriseController::show() should return Illuminate\Http\Response but returns Illuminate\Http\JsonResponse.
162 Method App\Http\Controllers\API\CmEnterpriseController::show() should return Illuminate\Http\Response but returns Illuminate\Http\JsonResponse.
176 Method App\Http\Controllers\API\CmEnterpriseController::update() should return Illuminate\Http\Response but returns Illuminate\Http\JsonResponse.
196 Expression in empty() is not falsy.
199 Method App\Http\Controllers\API\CmEnterpriseController::update() should return Illuminate\Http\Response but returns Illuminate\Http\JsonResponse.
201 Property App\Models\CmEnterprise::$enterprise (string|null) does not accept mixed.
202 Property App\Models\CmEnterprise::$description (string) does not accept mixed.
203 Property App\Models\CmEnterprise::$contact (string) does not accept mixed.
204 Property App\Models\CmEnterprise::$estate (string) does not accept mixed.
205 Property App\Models\CmEnterprise::$elanguage (string) does not accept mixed.
206 Property App\Models\CmEnterprise::$country (string) does not accept mixed.
207 Property App\Models\CmEnterprise::$currency (string) does not accept mixed.
215 Method App\Http\Controllers\API\CmEnterpriseController::update() should return Illuminate\Http\Response but returns Illuminate\Http\JsonResponse.
229 Method App\Http\Controllers\API\CmEnterpriseController::destroy() should return Illuminate\Http\Response but returns Illuminate\Http\JsonResponse.
240 Comparison operation ">" between 0 and 0 is always false.
241 Method App\Http\Controllers\API\CmEnterpriseController::destroy() should return Illuminate\Http\Response but returns Illuminate\Http\JsonResponse.
252 Method App\Http\Controllers\API\CmEnterpriseController::destroy() should return Illuminate\Http\Response but returns Illuminate\Http\JsonResponse.
------ ----------------------------------------------------------------------------------------------------------------------------------------------------
------ -------------------------------------------------------------------------------------------------------------------------------------
Line Http/Controllers/CmCustomerController.php
------ -------------------------------------------------------------------------------------------------------------------------------------
17 Method App\Http\Controllers\CmCustomerController::index() should return Illuminate\Http\Response but return statement is missing.
27 Method App\Http\Controllers\CmCustomerController::create() should return Illuminate\Http\Response but return statement is missing.
38 Method App\Http\Controllers\CmCustomerController::store() should return Illuminate\Http\Response but return statement is missing.
49 Method App\Http\Controllers\CmCustomerController::show() should return Illuminate\Http\Response but return statement is missing.
60 Method App\Http\Controllers\CmCustomerController::edit() should return Illuminate\Http\Response but return statement is missing.
72 Method App\Http\Controllers\CmCustomerController::update() should return Illuminate\Http\Response but return statement is missing.
83 Method App\Http\Controllers\CmCustomerController::destroy() should return Illuminate\Http\Response but return statement is missing.
------ -------------------------------------------------------------------------------------------------------------------------------------
------ ---------------------------------------------------------------------------------------------------------------------------------------
Line Http/Controllers/CmEnterpriseController.php
------ ---------------------------------------------------------------------------------------------------------------------------------------
17 Method App\Http\Controllers\CmEnterpriseController::index() should return Illuminate\Http\Response but return statement is missing.
27 Method App\Http\Controllers\CmEnterpriseController::create() should return Illuminate\Http\Response but return statement is missing.
38 Method App\Http\Controllers\CmEnterpriseController::store() should return Illuminate\Http\Response but return statement is missing.
49 Method App\Http\Controllers\CmEnterpriseController::show() should return Illuminate\Http\Response but return statement is missing.
60 Method App\Http\Controllers\CmEnterpriseController::edit() should return Illuminate\Http\Response but return statement is missing.
72 Method App\Http\Controllers\CmEnterpriseController::update() should return Illuminate\Http\Response but return statement is missing.
83 Method App\Http\Controllers\CmEnterpriseController::destroy() should return Illuminate\Http\Response but return statement is missing.
------ ---------------------------------------------------------------------------------------------------------------------------------------
------ ----------------------------------------------------------------------------------------
Line Http/Resources/CmCustomerResource.php
------ ----------------------------------------------------------------------------------------
21 Access to an undefined property App\Http\Resources\CmCustomerResource::$customer.
22 Access to an undefined property App\Http\Resources\CmCustomerResource::$customerstate.
23 Access to an undefined property App\Http\Resources\CmCustomerResource::$contact.
24 Access to an undefined property App\Http\Resources\CmCustomerResource::$sale.
25 Access to an undefined property App\Http\Resources\CmCustomerResource::$identerprise.
26 Access to an undefined property App\Http\Resources\CmCustomerResource::$paymentmethod.
27 Access to an undefined property App\Http\Resources\CmCustomerResource::$elanguage.
28 Access to an undefined property App\Http\Resources\CmCustomerResource::$currency.
29 Access to an undefined property App\Http\Resources\CmCustomerResource::$country.
30 Access to an undefined property App\Http\Resources\CmCustomerResource::$address.
------ ----------------------------------------------------------------------------------------
------ ----------------------------------------------------------------------------------------
Line Http/Resources/CmEnterpriseResource.php
------ ----------------------------------------------------------------------------------------
18 Access to an undefined property App\Http\Resources\CmEnterpriseResource::$enterprise.
19 Access to an undefined property App\Http\Resources\CmEnterpriseResource::$description.
20 Access to an undefined property App\Http\Resources\CmEnterpriseResource::$contact.
21 Access to an undefined property App\Http\Resources\CmEnterpriseResource::$estate.
22 Access to an undefined property App\Http\Resources\CmEnterpriseResource::$elanguage.
23 Access to an undefined property App\Http\Resources\CmEnterpriseResource::$country.
24 Access to an undefined property App\Http\Resources\CmEnterpriseResource::$currency.
------ ----------------------------------------------------------------------------------------
------ --------------------------------------------------------------------------------------------------
Line Jobs/CmCustomerCreated.php
------ --------------------------------------------------------------------------------------------------
16 Property App\Jobs\CmCustomerCreated::$customer has no type specified.
16 Property App\Jobs\CmCustomerCreated::$customer is never read, only written.
💡 See: https://phpstan.org/developing-extensions/always-read-written-properties
22 Method App\Jobs\CmCustomerCreated::__construct() has parameter $customer with no type specified.
------ --------------------------------------------------------------------------------------------------
------ --------------------------------------------------------------------------------------------------
Line Jobs/CmCustomerUpdated.php
------ --------------------------------------------------------------------------------------------------
17 Property App\Jobs\CmCustomerUpdated::$customer has no type specified.
17 Property App\Jobs\CmCustomerUpdated::$customer is never read, only written.
💡 See: https://phpstan.org/developing-extensions/always-read-written-properties
23 Method App\Jobs\CmCustomerUpdated::__construct() has parameter $customer with no type specified.
------ --------------------------------------------------------------------------------------------------
------ -------------------------------------------------
Line Providers/RouteServiceProvider.php
------ -------------------------------------------------
36 PHPDoc tag @var above a method has no effect.
36 PHPDoc tag @var does not specify variable name.
60 Cannot access property $id on mixed.
------ -------------------------------------------------
-- ----------------------------------------------------------------------------------------
Error
-- ----------------------------------------------------------------------------------------
Ignored error pattern #Unsafe usage of new static# was not matched in reported errors.
-- ----------------------------------------------------------------------------------------
[ERROR] Found 86 errors
Veamos el resultado aplicando el nivel 1:
$ ./vendor/bin/phpstan analyse
Note: Using configuration file /home/xules/xulprocx-local/DOCKER-Projects/Docker-Laravel/cx-laravel-microservices-customerdb-publish/cx-lpm-customerdb-admin/cx-lpm-customerdb-admin-app/phpstan.neon.
28/28 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
------ -------------------------------------------------------------------------------------------------------------------------------------
Line Http/Controllers/CmCustomerController.php
------ -------------------------------------------------------------------------------------------------------------------------------------
17 Method App\Http\Controllers\CmCustomerController::index() should return Illuminate\Http\Response but return statement is missing.
27 Method App\Http\Controllers\CmCustomerController::create() should return Illuminate\Http\Response but return statement is missing.
38 Method App\Http\Controllers\CmCustomerController::store() should return Illuminate\Http\Response but return statement is missing.
49 Method App\Http\Controllers\CmCustomerController::show() should return Illuminate\Http\Response but return statement is missing.
60 Method App\Http\Controllers\CmCustomerController::edit() should return Illuminate\Http\Response but return statement is missing.
72 Method App\Http\Controllers\CmCustomerController::update() should return Illuminate\Http\Response but return statement is missing.
83 Method App\Http\Controllers\CmCustomerController::destroy() should return Illuminate\Http\Response but return statement is missing.
------ -------------------------------------------------------------------------------------------------------------------------------------
------ ---------------------------------------------------------------------------------------------------------------------------------------
Line Http/Controllers/CmEnterpriseController.php
------ ---------------------------------------------------------------------------------------------------------------------------------------
17 Method App\Http\Controllers\CmEnterpriseController::index() should return Illuminate\Http\Response but return statement is missing.
27 Method App\Http\Controllers\CmEnterpriseController::create() should return Illuminate\Http\Response but return statement is missing.
38 Method App\Http\Controllers\CmEnterpriseController::store() should return Illuminate\Http\Response but return statement is missing.
49 Method App\Http\Controllers\CmEnterpriseController::show() should return Illuminate\Http\Response but return statement is missing.
60 Method App\Http\Controllers\CmEnterpriseController::edit() should return Illuminate\Http\Response but return statement is missing.
72 Method App\Http\Controllers\CmEnterpriseController::update() should return Illuminate\Http\Response but return statement is missing.
83 Method App\Http\Controllers\CmEnterpriseController::destroy() should return Illuminate\Http\Response but return statement is missing.
------ ---------------------------------------------------------------------------------------------------------------------------------------
------ ----------------------------------------------------------------------------------------
Line Http/Resources/CmCustomerResource.php
------ ----------------------------------------------------------------------------------------
21 Access to an undefined property App\Http\Resources\CmCustomerResource::$customer.
22 Access to an undefined property App\Http\Resources\CmCustomerResource::$customerstate.
23 Access to an undefined property App\Http\Resources\CmCustomerResource::$contact.
24 Access to an undefined property App\Http\Resources\CmCustomerResource::$sale.
25 Access to an undefined property App\Http\Resources\CmCustomerResource::$identerprise.
26 Access to an undefined property App\Http\Resources\CmCustomerResource::$paymentmethod.
27 Access to an undefined property App\Http\Resources\CmCustomerResource::$elanguage.
28 Access to an undefined property App\Http\Resources\CmCustomerResource::$currency.
29 Access to an undefined property App\Http\Resources\CmCustomerResource::$country.
30 Access to an undefined property App\Http\Resources\CmCustomerResource::$address.
------ ----------------------------------------------------------------------------------------
------ ----------------------------------------------------------------------------------------
Line Http/Resources/CmEnterpriseResource.php
------ ----------------------------------------------------------------------------------------
18 Access to an undefined property App\Http\Resources\CmEnterpriseResource::$enterprise.
19 Access to an undefined property App\Http\Resources\CmEnterpriseResource::$description.
20 Access to an undefined property App\Http\Resources\CmEnterpriseResource::$contact.
21 Access to an undefined property App\Http\Resources\CmEnterpriseResource::$estate.
22 Access to an undefined property App\Http\Resources\CmEnterpriseResource::$elanguage.
23 Access to an undefined property App\Http\Resources\CmEnterpriseResource::$country.
24 Access to an undefined property App\Http\Resources\CmEnterpriseResource::$currency.
------ ----------------------------------------------------------------------------------------
-- ----------------------------------------------------------------------------------------
Error
-- ----------------------------------------------------------------------------------------
Ignored error pattern #Unsafe usage of new static# was not matched in reported errors.
-- ----------------------------------------------------------------------------------------
[ERROR] Found 32 errors
3. Corregimos errores y mejoramos el código
Partimos inicialmente en el análisis más exigente de 86 errores. No lo repetiré a partir de ahora, pero cada vez que hacemos una corrección, ejecutamos el comando analyse de PHPStan de la siguiente manera:
xules@XXXXXXX:~/....../cx-lpm-customerdb-admin-app$./vendor/bin/phpstan analyse
PHPStan finds bugs in your code without writing tests.
https://phpstan.org/
Vamos al lío:
Error 1: should return Illuminate\Http\Response but returns Illuminate\Http\JsonResponse
Este error tiene que ver con la documentación que añadimos a nuestros métodos, en este caso en la generación de código por defecto de Laravel se asigna que la respuesta en el controlador es de tipo Illuminate\Http\Response , pero nosotos en este controlador hemos desarrollado una API con lo que la respuesta real será de tipo Illuminate\Http\JsonResponse.
Cómo veis en este caso no es un error de programación pero si de documentación, y que es tan importante, ya que si alguién utilizada nuestro código y le damos información errónea tenemos un problema.
34 Method App\Http\Controllers\API\CmCustomerController::index() should return Illuminate\Http\Response but returns Illuminate\Http\JsonResponse.
Solución
Sustituir todos los métodos de la API la respuesta incorrecta con la correcta, por ejemplo en index() de CmCustomerController:
Pasamos de:
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
A:
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\JsonResponse devuelve un Json con los datos de clientes.
*/
public function index()
Si lanzamos de nuevo el análisis del código comprobamos que ahora el número de errores se ha reducido a 68 errores.
Error 2: Property App\Models\CmCustomer::property does not accept mixed
En este caso se está detectando un error sobre las propiedas definidas en CmCustomer que están predefinidas con la estructura de stdClass, en la documentación de PHPStan en el apartado Universal Object Creates donde hacen referencia directamente a stdClass y a otras estructuras generadas por los framework PHP para que indiquemos en el fichero phpstan.neon para que se tenga en cuenta el uso de eso tipo de clases.
Estos errores se indican tanto en
Line Http/Controllers/API/CmCustomerController.php
------ -----------------------------------------------------------------------------------------
150 Property App\Models\CmCustomer::$identerprise (int) does not accept mixed.
151 Property App\Models\CmCustomer::$customer (string) does not accept mixed.
152 Property App\Models\CmCustomer::$contact (string) does not accept mixed.
153 Property App\Models\CmCustomer::$customerstate (string) does not accept mixed.
154 Property App\Models\CmCustomer::$paymentmethod (string) does not accept mixed.
155 Property App\Models\CmCustomer::$elanguage (string) does not accept mixed.
156 Property App\Models\CmCustomer::$country (string) does not accept mixed.
157 Property App\Models\CmCustomer::$currency (string) does not accept mixed.
158 Property App\Models\CmCustomer::$address (string) does not accept mixed.
Solución
Añadimos el parámetro de configuración universalObjectCratesClasses tal y cómo se indica en la configuración de la siguiente forma:
parameters:
..............
universalObjectCratesClasses:
- App\Models\CmCustomer
- App\Models\CmEnterprise
Si lanzamos de nuevo el análisis del código comprobamos que ahora el número de errores se ha reducido a 52 errores.
Error 3: 17 Method App\Http\Controllers\CmCustomerController::index() should return Illuminate\Http\Response but return statement is missing
Este error afecta a todos los métodos de las clases App\Http\Controllers\CmCustomerController y App\Http\Controllers\CmEnterpriseController que hemos creado y que no se van a desarrollar en este proyecto, entonces o los eliminamos o le indicamos a Larastan que no tenga en cuenta en el análisis estás clases.
Como estamos aprendiendo a utilizar Larastan vamos a ver como indicarle que no analice estas clases:
17 Method App\Http\Controllers\CmCustomerController::index() should return Illuminate\Http\Response but return statement is missing
En la configuración de phpstan.neon podemos excluir este fichero a analizar porque aún no está desarrollado, de la siguiente forma:
excludePaths:
excludePaths:
- ./*/*/FileToBeExcluded.php
- app/Http/Controllers/CmEnterpriseController.php
- app/Http/Controllers/CmCustomerController.php
Con está actualización dejamos ya los errores en el código en 38 errors
Error 4: Access to an undefined property App\Http\Resources\CmCustomerResource::$property
En este caso el error no es tal, ya que en la definición de CmCustomerResource que extiende de JSonResource estamos utilizando la estructura base que nos proporciona el método, si tuviesemos que definir todas las propiedades nos veriamos obligados a redifinir la clase al completo, y ese no es el objetivo, veamos como nos indica PHPStan que definamos estas propiedades.
Primero vemos los errores que nos saltan en dos clases App\Http\Resources\CmCustomerResource y App\Http\Resources\CmEnterpriseResource:
------ ----------------------------------------------------------------------------------------
Line Http/Resources/CmCustomerResource.php
------ ----------------------------------------------------------------------------------------
21 Access to an undefined property App\Http\Resources\CmCustomerResource::$customer.
22 Access to an undefined property App\Http\Resources\CmCustomerResource::$customerstate.
23 Access to an undefined property App\Http\Resources\CmCustomerResource::$contact.
24 Access to an undefined property App\Http\Resources\CmCustomerResource::$sale.
25 Access to an undefined property App\Http\Resources\CmCustomerResource::$identerprise.
26 Access to an undefined property App\Http\Resources\CmCustomerResource::$paymentmethod.
27 Access to an undefined property App\Http\Resources\CmCustomerResource::$elanguage.
28 Access to an undefined property App\Http\Resources\CmCustomerResource::$currency.
29 Access to an undefined property App\Http\Resources\CmCustomerResource::$country.
30 Access to an undefined property App\Http\Resources\CmCustomerResource::$address.
------ ----------------------------------------------------------------------------------------
------ ----------------------------------------------------------------------------------------
Line Http/Resources/CmEnterpriseResource.php
------ ----------------------------------------------------------------------------------------
18 Access to an undefined property App\Http\Resources\CmEnterpriseResource::$enterprise.
19 Access to an undefined property App\Http\Resources\CmEnterpriseResource::$description.
20 Access to an undefined property App\Http\Resources\CmEnterpriseResource::$contact.
21 Access to an undefined property App\Http\Resources\CmEnterpriseResource::$estate.
22 Access to an undefined property App\Http\Resources\CmEnterpriseResource::$elanguage.
23 Access to an undefined property App\Http\Resources\CmEnterpriseResource::$country.
24 Access to an undefined property App\Http\Resources\CmEnterpriseResource::$currency.
------ ----------------------------------------------------------------------------------------
Lo que hacemos en este caso es definir las clases en la documentación de cabecera de la siguiente forma:
- App\Http\Resources\CmCustomerResource:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
use App\Models\CmEnterprise;
use App\Http\Resources\CmEnterpriseResource;
/**
*
* @property string $customer.
* @property string $customerstate.
* @property string $contact.
* @property string $sale.
* @property int $identerprise.
* @property string $paymentmethod.
* @property string $elanguage.
* @property string $currency.
* @property string $country.
* @property string $address
*/
class CmCustomerResource extends JsonResource
{
- App\Http\Resources\CmEnterpriseResource:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
/**
* @property string $enterprise.
* @property string $description.
* @property string $contact.
* @property string $estate.
* @property string $elanguage.
* @property string $country.
* @property string $currency.
*/
class CmEnterpriseResource extends JsonResource
{
Con está actualización dejamos ya los errores en el código en 21 errors.
Error 5: Expression in empty() is not falsy
El uso del método empty() tiene cierta ambigüedad ya que vale para cualquier tipo de objeto como se puede ver en la documentación de PHP empty() y además no genera una advertencia si la variable no ha sido declarada.
Entonces si analizamos el error en el uso en el código, podemos comprobar que el uso de este método pude dar lugar a generar algún fallo o no ser claro aunque el código funcione, error:
if($validator->fails()){
$response = [
'success' => false,
'message' => 'Validation Error.'
];
if(!empty($validator->errors())){
$response['data'] = $validator->errors();
}
return response()->json($response, 404);
}
Una forma más correcta de controlar si tenemos información sobre los errores para devolver sería utilizar el método count(array) que nos devolverá 0 si no hay valores en el array, otra solución sería eliminar este if y devolver el array de errores siempre aunque este vacío.
Solución: sustituimos el uso del método empty() en los arrays por count():
Los errores los teníamos aquí:
------ -----------------------------------------------------------------------------------------
Line Http/Controllers/API/CmCustomerController.php
------ -----------------------------------------------------------------------------------------
58 Expression in empty() is not falsy.
145 Expression in empty() is not falsy.
------ -----------------------------------------------------------------------------------------
Line Http/Controllers/API/CmEnterpriseController.php
------ -----------------------------------------------------------------------------------------
99 Expression in empty() is not falsy.
196 Expression in empty() is not falsy.
Vemos la utilización del método count en el caso anterior de Http/Controllers/API/CmCustomerController.php:
if($validator->fails()){
$response = [
'success' => false,
'message' => 'Validation Error.'
];
if(count($validator->errors()) !== 0){
$response['data'] = $validator->errors();
}
return response()->json($response, 404);
}
Con esta correción estaríamos ya con solo 17 errores
Error 6: corregimos varios errores en la documentación de métodos
Error 6.1 Error en la documentación del método destroy de CmCustomerController.php:
------ -----------------------------------------------------------------------------------------
Line Http/Controllers/API/CmCustomerController.php
------ -----------------------------------------------------------------------------------------
180 Method App\Http\Controllers\API\CmCustomerController::destroy() has parameter $idcustomer with no type specified.
180 PHPDoc tag @param references unknown parameter: $id
La solución es fácil nuestro parámetro se llama $idcustomer y lo hemos documentado como $id, esto genera un error en la documentación.
Cambiamos la documentación de :
/**
* Remove the specified resource from storage.
* Eliminamos el recurso específicado del almacenamiento.
* @param int $id
* @return \Illuminate\Http\JsonResponse
*/
public function destroy($idcustomer)
{
A:
/**
* Remove the specified resource from storage.
* Eliminamos el recurso específicado del almacenamiento.
* @param int $idcustomer
* @return \Illuminate\Http\JsonResponse
*/
public function destroy($idcustomer)
{
Ahora nos quedamos con 15 errores.
Error 6.2: vemos que tenemos errores en la clase Providers/RouteServiceProvider.php que es generada automáticamente por Laravel
Como no hemos desarrollado código de momento la vamos a excluir de nuestro análisis, si bien los errores no son significativos ya que PHPDoc hace referencia a una variable que está comentada y entonces no tendría sentido la documentación, y en el caso de $id tendríamos que añadir la definición de la variable.
Estos son los mensajes de error:
------ -------------------------------------------------
Line Providers/RouteServiceProvider.php
------ -------------------------------------------------
38 PHPDoc tag @var above a method has no effect.
38 PHPDoc tag @var does not specify variable name.
62 Cannot access property $id on mixed.
------ -------------------------------------------------
Los añadimos entre los ficheros que no queremos analizar en el fichero phpstan.neon:
excludePaths:
- ./*/*/FileToBeExcluded.php
- app/Http/Controllers/CmEnterpriseController.php
- app/Http/Controllers/CmCustomerController.php
- app/Providers/RouteServiceProvider.php
Eliminando este clase del análisis nos quedamos con 12 errores.
Error 7: Corregimos errores de programación en app/Http/Controllers/API/CmCustomerController.php
Vamos a corregir los últimos errores notificados en la clase app/Http/Controllers/API/CmCustomerController.php donde en el método de código hemos cometido un error de programación al no contemplar que un objeto pueda ser null lo que generaría que ser rompiera nuestro código y nuestra Api no diera una respuesta ante ese error.
Estos errores nos los indica así PHPStan:
------ -----------------------------------------------------------------
Line Http/Controllers/API/CmCustomerController.php
------ -----------------------------------------------------------------
183 Cannot access property $customer on App\Models\CmCustomer|null.
185 Cannot call method delete() on App\Models\CmCustomer|null.
------ -----------------------------------------------------------------
Si nos vamos al método desarrollado detectamos rápidamente el control que se nos ha olvidado establecer, y encontramos rápidamente un solución sencilla, esto demuestra la gran utilidad de herramientas como PHPStan para controlar nuestro código:
/**
* Remove the specified resource from storage.
* Eliminamos el recurso específicado del almacenamiento.
* @param int $idcustomer
* @return \Illuminate\Http\JsonResponse
*/
public function destroy($idcustomer)
{
$customer = CmCustomer::find($idcustomer);
$lcustomer = $customer->customer;
// No podremos borrar el cliente si tiene pedidos asignados:
$customer->delete();
$response = [
'success' => true,
'message' => 'El cliente '.$lcustomer.' se ha borrado correctamente',
];
return response()->json($response, 200);
}
Como se puede ver donde marcamos en negrita el código anterior, ¿qué pasa si nos solicitan borrar un id que no existe? , pues esto lo detectamos con PHPStan. Tengamos en cuenta que estos son ejemplos para analizar el funcionamiento de PHPStan, ya que un error de este tipo lo deberíamos detectar nosotros mismo si desarrollamos correctamente los test unitarios.
La solución es sencilla controlamos si no existe el CmCustomer asociado al id solicitado y si es así devolvemos un error indicando cómo:
/**
* Remove the specified resource from storage.
* Eliminamos el recurso específicado del almacenamiento.
* @param int $idcustomer
* @return \Illuminate\Http\JsonResponse
*/
public function destroy($idcustomer)
{
$customer = CmCustomer::find($idcustomer);
if ($customer !== null) {
$lcustomer = $customer->customer;
// No podremos borrar el cliente si tiene pedidos asignados:
$customer->delete();
$response = [
'success' => true,
'message' => 'El cliente '.$lcustomer.' se ha borrado correctamente',
];
return response()->json($response, 200);
} else {
return response()->json(
$response = [
'success' => false,
'message' => 'No se ha encontrado el cliente con id:'.$idcustomer.' que se quería eliminar'
],
404);
}
}
Ya casi estamos acando ahora ya solo nos quedan 10 errores que corregir.
Error 8: Corregimos errores de programación en app/Http/Controllers/API/CmEnterpriseController.php
Los errores que tenemos aquí son fáciles de solucionar, el primero en la línea 86 hacer referencia a un problema con la documentación, y el otro de código es una tarea que dejamos sin hacer a propósito, veamos los errores:
------ -----------------------------------------------------------------------------------------
Line Http/Controllers/API/CmEnterpriseController.php
------ -----------------------------------------------------------------------------------------
86 Method App\Http\Controllers\API\CmEnterpriseController::store() has no return type specified.
240 Comparison operation ">" between 0 and 0 is always false.
------ -----------------------------------------------------------------------------------------
El primero es muy sencillo de resolver y consiste en que hemos iniciado mal los comentarios de documentación:
/*
* Store a newly created resource in storage.
* Crea un nuevo registro en la base de datos.
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse devolvemos un objeto JsonResponse
*
*/
public function store(Request $request)
{
Como podemos ver nos falta un * al inicio de la documentación lo añadimos y solucionado el error:
/**
* Store a newly created resource in storage.
* Crea un nuevo registro en la base de datos.
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse devolvemos un objeto JsonResponse
*
*/
public function store(Request $request)
{
El segundo hacer referencia a una parte que dejamos sin impletar, voy a mostrar solo la lógica de ese punto y no todo el método ya que se entiende perfectamente:
$numCustomers = 0;
// Necesitamos comprobar la integridad del borrado, si se utiliza en clientes no se puede borrar.
if ($numCustomers > 0) {
$numCustomers = 0;
$numCustomers = CmCustomer::where('identerprise', '=', $enterprise->identerprise)->count();
if ($numCustomers > 0) {
Cómo se puede ver PHPStan nos está diciendo lo evidente que el resultado del if siempre es false ya que la variable siempre es cero, este ejemplo es ilustrativo de algo que nos puede pasar en el desarrollo real, por ejemplo, está condición no estableció al desarrollar CmEnterpriseController ya que el número de clientes lo obtenemos de la clase CmCustomer, y está no estaba definida en este punto, cómo ya le tenemos podemos completar nuestro código de la siguiente forma:
Corregitos estos errores vamos a por los últimos 8 errores a corregir.
Error 9: Corregimos errores de tipado en app/Jobs/CmCustomerCreated.php y en app/Jobs/CmCustomerUpdated.php
------ -----------------------------------------------------------------------------------------
Line Jobs/CmCustomerCreated.php
------ -----------------------------------------------------------------------------------------
18 Property App\Jobs\CmCustomerCreated::$customer has no type specified.
18 Property App\Jobs\CmCustomerCreated::$customer is never read, only written.
💡 See: https://phpstan.org/developing-extensions/always-read-written-properties
24 Method App\Jobs\CmCustomerCreated::__construct() has parameter $customer with no type specified.
------ -----------------------------------------------------------------------------------------
class CmCustomerCreated implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private $customer;
/**
* Create a new job instance.
* Creamos una nueva instancia.
* @return void
*/
public function __construct($customer)
{
$this->customer = $customer;
}
dadfasdf
class CmCustomerCreated implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private Array $customer;
/**
* Create a new job instance.
* Creamos una nueva instancia.
* @return void
*/
public function __construct(Array $customer)
{
$this->customer = $customer;
}
Corregimos añadimos la identificación para la variable $customer:
Corregimos errores en app/Jobs/CmCustomerUpdated.php:
------ -----------------------------------------------------------------------------------------
Line Jobs/CmCustomerUpdated.php
------ -----------------------------------------------------------------------------------------
17 Property App\Jobs\CmCustomerUpdated::$customer has no type specified.
17 Property App\Jobs\CmCustomerUpdated::$customer is never read, only written.
💡 See: https://phpstan.org/developing-extensions/always-read-written-properties
23 Method App\Jobs\CmCustomerUpdated::__construct() has parameter $customer with no type specified.
------ -----------------------------------------------------------------------------------------
class CmCustomerUpdated implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private $customer;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($customer)
{
$this->customer = $customer;
}
Al finalizar la corrección de los errores de tipado
Error 10: Property is never read, only written en app/Jobs/CmCustomerCreated.php y app/Jobs/CmCustomerUpdated.php
Este error es debido a que hemos definido una propiedad como privada en nuestro caso $customer y nos indica que nunca es leída, sino solo escrita, para más información de este error tienes este enlace de PHPStan https://phpstan.org/developing-extensions/always-read-written-properties. El error que nos indican ahora es:
------ ----------------------------------------------------------------------------------
Line Jobs/CmCustomerCreated.php
------ ----------------------------------------------------------------------------------
18 Property App\Jobs\CmCustomerCreated::$customer is never read, only written.
💡 See: https://phpstan.org/developing-extensions/always-read-written-properties
------ ----------------------------------------------------------------------------------
------ ----------------------------------------------------------------------------------
Line Jobs/CmCustomerUpdated.php
------ ----------------------------------------------------------------------------------
18 Property App\Jobs\CmCustomerUpdated::$customer is never read, only written.
💡 See: https://phpstan.org/developing-extensions/always-read-written-properties
------ ----------------------------------------------------------------------------------
Veamos el código donde se da este error, solo mostraré el de CmCustomerCreated ya que el otro es idéntico:
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App\Models\CmCustomer;
class CmCustomerUpdated implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private Array $customer;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(Array $customer)
{
$this->customer = $customer;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
//
}
}
Solución
Nosotros vamos a ver posibles soluciones dentro del código sin recurrir a la aplicación de la extensión que nos indica PHPStan:
- Una solución sería hacer la variable $customer púbica, lo cual no tiene mucho sentido abrir a que se modifique esta propiedad si solo queremos que se modifique a través del constructor.
- La otra solución más óptima es añadir un getter para la variable $customer, con lo que la variable privada ya es leída dentro de la clase y ya tenemos nuestro código correcto.
/**
* @return Array devolvemos los datos del cliente en un array.
*/
public function getCustomer() {
return $this->customer;
}
Después de añadir este método en las dos clases, el número de errores que tenemos es 2: Found errors 2.
Los últimos errores serían:
$ ./vendor/bin/phpstan analyse
Note: Using configuration file /.........../cx-lpm-customerdb-admin/cx-lpm-customerdb-admin-app/phpstan.neon.
25/25 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
-- ---------------------------------------------------------------------------------------------
Error
-- -----------------------------------------------------------------------------------------------
Ignored error pattern #Unsafe usage of new static# was not matched in reported errors.
Ignored error pattern #should return Illuminate\Http\Response but return statement is missing# in path
/home/xules/xulprocx-local/DOCKER-Projects/Docker-Laravel/cx-laravel-microservices-customerdb-publish/cx-lpm-customerdb-admin/cx-lpm-customerdb-admin-app/App/Http/Cont
rollers/CmCustomerController was not matched in reported errors.
-- ---------------------------------------------------------------------------------------------
[ERROR] Found 2 errors