Laravel 4: loading Service Provider at runtime - php

On first thought, this may seem unnecessary, as we have defined providers in config/app.php to autoload any ServiceProvider, but it turns out there is a scenario where they will NOT be autoloaded:
When we run a job from Laravel Queue - it would seem my ServiceProvider in config are ignore completely, so DI failed with target ... is not instantiable.
Register my service providers at runtime in the job does work. e.g.
App::register('MyServiceProvider');
Is there a reason that Laravel did not autoload my ServiceProvider in such case?
PS: I opened an issue on github as well, as I am not if this is by design.

If you define your environments by URL, those environments will not be automatically recognised from the command line - I've run into this issue myself when trying to run migrations/seeds.
You can define environments in any way you like since the environment definition accepts a closure but 'out of the box' you can return a regex that matches wither a machine name or a url. examples here - environment config.
One solution would be to define your service providers in the app.php at the route of your config (this is the default config and will be used if no other environments are recognised from the command line) OR if you need different settings for different environments you could try defining your environments by machine name - this is the hostname of your machine - on a unix box you can see what this is with echo $HOSTNAME on the command line.
Another solution from OP
As the OP has discovered, artisan accepts --env flag on just about every command which allows you to force an environment, so you can call php artisan queue:work --env=local to force it to use the local config when working queues.
Hope this helps

Related

Laravel Debugbar Not Showing

I have a project with Laravel version 7.28. I run
composer require barryvdh/laravel-debugbar --dev,
After that I added Barryvdh\Debugbar\ServiceProvider::class to app/config.php under providers and added
'Debugbar' => Barryvdh\Debugbar\Facade::class
to app/config.php under aliases. Then I run
php artisan vendor:publish --provider="Barryvdh\Debugbar\ServiceProvider"
Even though APP_DEBUG in .env is true and I terminated and rerun the app debugbar is not showing. Where did I make my mistake?
I checked the config/debugbar.php, I see the new key DEBUGBAR_ENABLED with default is false. Then add it to .env file: DEBUGBAR_ENABLED=true
Make sure you are running on right port. I had php backend (running on port 8000) and react frontend (running on port 3000) and I did everything you have described above but it never worked. The issue I had was that I was checking if the debugbar showed up on port 3000 (which it never did sadly). I went to check on port 8000 and the debugbar was sitting there the whole time.
I didn't end up using the php debugbar because I think it only works if your frontend code is written in php (though not 100% sure).
I ended up debugging using postman. It doesn't have all the functionalities that the debugbar offers but it is a great tool to use if you just want to do basic debugging.

Gitlab CI configuration with Laravel Docker Redis wrong host or port

I'm working on a Gitlab CI configuration in the .gitlab-ci.yml file for a Laravel project which uses redis as Cache Driver. Within this configuration file I use an image which has all the linux packages, php extension which are used on our production environment. This includes the redis extension installed by pecl so phpredis can be used within Laravel.
After spending a fair amount of time into learning the process of continuous integration and the configuration of the gitlab ci file I encountered the following error on deploy of the image:
In PhpRedisConnector.php line 126:
Redis::connect() expects parameter 2 to be int, string given
parameter should be only the port as an int but somehow turned into tcp://111.111.11.11:6379
As documented https://docs.gitlab.com/ee/ci/services/redis.html the host in your .env should be redis so I had. Dumping the config during the deploy resulted in port being some tcp://111.111.11.11:6379 connection string and host being empty. There is no way this is changed within our application or any extension we use.
I use the https://hub.docker.com/_/redis/ as a service in the .gitlab-ci.yml.
...
services:
- name: redis:4
...
Solution found here:
https://laracasts.com/discuss/channels/testing/gitlab-ci-weird-redis-host
This variable isn't documented anywhere but somehow it solves the problem.
Add the following to your .gitlab-ci.yml file:
variables:
REDIS_PORT: 6379
If anyone finds documentation about this variable and maybe more options, let me know!
This is expected behaviour from Docker.
In Gitlab CI, all services you define will be linked with your container which is running your job using Docker linking system.
As describe here, when we link containers, Docker automatically creates ENVs in format:
<_alias>_NAME
<_alias>_PORT
and some other ENVS
And those ENVs from Docker will override yours.
alias here will be the hostname of your container you defined in services. For example you define these services in your .gitlab-ci.yml:
services:
- redis:5-alpine
- name: mongo
alias: db
So Docker will create the following ENVs respectively:
REDIS_PORT="some thing looks like: tcp://172.17.0.3:6379"
REDIS_NAME="looks like: /runner-72989761-project-19846207-concurrent-0-62507216079cf651-build-3/redis"
DB_PORT=same like redis
DB_NAME=same like redis
And note that, as described here, by default the following ENVs are also auto created based on service image's name:
MONGO_PORT=...
MONGO_NAME=...
So in your code if you have any variable that has same name with the ones Docker creates, you may need to change your variable's name otherwise it'll be override by Docker. Or you can use the solution from #MmynameStackflow above, pass variables in your Gitlab CI config file to override what Docker does.

PHPUnit - Class '/path/to/my/class.php' could not be found in ''

I have set up PHPUnit on Windows to do unit testing within my Moodle application. My php version is 7.2.10 and the version of PHPUnit is 7.5.
I was able to successfully install composer and then PEAR and PHPUnit.
I was then able to initialise the Moodle PHPUnit test environment by running:
php ../phpunit/cli/init.php
This successfully set up the test environment and ran all of the unit tests in the application.
The problem I have is when I attempt to run a single test, using e.g.
vendor\bin\phpunit /mod/quiz/tests/event_test.php
This gives me the following error:
Class '/mod/quiz/tests/event_test.php' could not be found in ''.
The solution to a similar looking problem, PHPUnit error - Class could not be found, does not solve the issue. I had to modify the solution since the StandardTestSuiteLoader class has changed - I tried making all comparisons to realpath($suiteClassFile) case insensitive but this has not worked.
I'm not overly familiar with running phpunit against a file path as opposed to a class name, but is this expecting an absolute path?
/mod/quiz/tests/event_test.php
Is probably trying to be loaded from the root directory. Either try dropping the leading /, or supply the full windows file path, including C:

You have requested a non-existent service "phpexcel"

I know there are some questions almost identical but none of them seems to be my case.
I have a symfony 2.8.3 project that reads and imports data from an excel file into mysql database. Its all working nice on localhost but in the last 48 hours I've been trying to get it working on my server. Its a shared hosting, with no SSH access to the linux.
When I am trying to load it from the server I get this error: "You have requested a non-existent service "phpexcel"."
Looks like you want to use service from ExcelBundle. But that bundle is not loaded. Check if you have it added for production env.
$bundles = array(
// ...
new Liuggio\ExcelBundle\LiuggioExcelBundle(),
);
Don't forget to clear cache on production environment after any config (AppKernel.php also) change.
To clear cache run php app/console cache:clear. You can also add env parameter: --env=dev or --env=prod - depending on your env. If it don't help then just remove all content of app/cache/ directory (or var/cache/ in case of Symfony3 app)
Pawel answered correctly, but something is missing: after you add this line: new Liuggio\ExcelBundle\LiuggioExcelBundle(), to the AppKernel.php file, inside the $bundles array, don't forget to clear the cache: delete the file from app/cache/dev in case you're in developer mode or app/cache/prod in case on production mode.

Creating a new ServiceProvider / Facade as a package in Laravel 5

Introduction
I've never worked with a framework before (Zend, CakePHP, etc) and finally decided to sit down and learn one. I'm starting with Laravel because the code looks pretty and unlike some other frameworks I tried to install, the "Hello, World!" example worked on the first try.
The Goal
For the time being, I want my app to do something very simple:
User submits a request in the form of: GET /dist/lat,lng
The app uses the remote IP address and MaxMind to determine $latitude1 and $longitude1
This request path is parsed for $latitude2 and $longitude2
Using these two positions, we calculate the distance between them. To do this I'm using Rafael Fragoso's WorldDistance PHP class
Since I plan to re-use this function in later projects, it didn't seem right to throw all of the code into the /app directory. The two reusable parts of the application were:
A service provider that connects to MaxMind and returns a latitude and longitude
A service provider that takes two points on a globe and returns the distance
If I build facades correctly then instead of my routes.php file being a mess of closures within closures, I can simply write:
Route::get('dist/{input}', function($input){
$input = explode( "," , $input );
return Distance::getDistance( GeoIP::getLocation(), $input );
});
What I've tried
Initial Attempt
For the first service provider, I found Daniel Stainback's Laravel 5 GeoIP service provider. It didn't install as easily as it should have (I had to manually copy geoip.php to the /config directory, update /config/app.php by hand, and run composer update and php artisan optimize) however it worked: A request to GET /test returned all of my information.
For the second service provider, I started by trying to mimic the directory structure and file naming convention of the GeoIP service provider. I figured that if I had the same naming convention, the autoloader would be able to locate my class. So I created /vendor/stevendesu/worlddistance/src/Stevendesu/WorldDistance\WorldDistanceServiceProvider.php:
<?php namespace Stevendesu\WorldDistance;
use Illuminate\Support\ServiceProvider;
class WorldDistanceServiceProvider extends ServiceProvider {
protected $defer = false;
public function register()
{
// Register providers.
$this->app['distance'] = $this->app->share(function($app)
{
return new WorldDistance();
});
}
public function provides()
{
return ['distance'];
}
}
I then added this to my /config/app.php:
'Stevendesu\WorldDistance\WorldDistanceServiceProvider',
This fails with a fatal error:
FatalErrorException in ProviderRepository.php line 150:
Class 'Stevendesu\WorldDistance\WorldDistanceServiceProvider' not found
Using WorkBench
Since this utterly failed I figured that there must be some other file dependency: maybe without composer.json or without a README it gives up. I don't know. So I started to look into package creation. Several Google searches for "create package laravel 5" proved fruitless. Either:
They were using Laravel 4.2, in which case the advice was "run php artisan workbench vendor/package --resources"
Or
They were using Laravel 5, in which case the docs were completely useless
The official Laravel 5 docs give you plenty of sample code, saying things like:
All you need to do is tell Laravel where the views for a given namespace are located. For example, if your package is named "courier", you might add the following to your service provider's boot method:
public function boot()
{
$this->loadViewsFrom(__DIR__.'/path/to/views', 'courier');
}
This makes the assumption that you have a service provider to put a boot method in
Nothing in the docs says how to create a service provider in such a way that it will actually be loaded by Laravel.
I also found several different resources all of which assume you have a repository and you just want to include it in your app, or assume you have "workbench". Nothing about creating a new package entirely from scratch.
PHP Artisan did not even have a "workbench" command, and there was no "workbench.php" file in /config, so anything I found related to workbench was worthless. I started doing some research on Workbench and found several different questions on StackOverflow.
After a long time and some experimentation, I managed to get laravel/workbench into my composer.json, composer update, composer install, manually build a workbench.php config file, and finally use the PHP Artisan Workbench command to make a new package:
php artisan workbench Stevendesu/WorldDistance --resources
This created a directory: /workbench/stevendesu/world-distance with a number of sub-directories and only one file: /workbench/stevendesu/world-distance/src/Stevendesu/WorldDistance/WorldDistanceServiceProvider.php
This service provider class looked essentially identical to the file I created before, except that it was in the /workbench directory instead of the /vendor directory. I tried reloading the page and I still got the fatal error:
FatalErrorException in ProviderRepository.php line 150:
Class 'Stevendesu\WorldDistance\WorldDistanceServiceProvider' not found
I also tried php artisan vendor:publish. I don't really know what this command does and the description wasn't helpful, so maybe it would help? It didn't.
Question
How do I create a new service provider as a package so that in future projects I can simply include this package and have all the same functionality? Or rather, what did I do wrong so that the package I created isn't working?
After two days of playing with this I managed to find the solution. I had assumed that the directory structure mapped directly to the autoloader's path that it checked (e.g. attempting to access a class Stevendesu\WorldDistance\WorldDistanceServiceProvider would look in vendor/stevendesu/world-distance/WorldDistanceServiceProvider)... This isn't the case.
Reading through the composer source code to see how it actually loads the files, it builds a "classmap" - essentially a gigantic array mapping classes to their respective files. This file is built when you run composer update or composer install - and it will only be built correctly if composer knows the details of your package. That is - if your package is included in your project's composer.json file
I created a local git repository outside of my app then added my package to my app's composer.json file then ran composer update -- suddenly everything worked perfectly.
As for the:
It didn't install as easily as it should have
the secret sauce here was first add the service provider to /config/app.php then, second run php artisan vendor:publish

Categories