Maybe dumb question, I'm new to Symfony2 and I'm using it for one of my projects.
I'd like to be able to use a third party library, namely SSRSReport (an API for SSRS Reports).
I have put the library into Symfony/vendor/ssrs/lib/Ssrs/src.
There are many classes defined here, I don't need them to be autoloaded.
I simply don't know how to require and call them from a controller.
For sure this doesn't work
require_once '/vendor/ssrs/lib/Ssrs/src/SSRSReport.php';
class DefaultController extends Controller
{
public function viewAction()
{
define("UID", "xxxxxxxx");
define("PASWD", "xxxxxxxx");
define("SERVICE_URL", "http://xxx.xxx.xxx.xxx/ReportServer/");
$report = new SSRSReport(new Credentials(UID, PASWD), SERVICE_URL);
return $this->render('myBundle:Default:view.html.twig'
, array('report' => $report)
);
}
}
SSRSReport() and Credentials() used here, are 2 of many classes contained into the API.
First of all, I don't recommend putting non-symfony-managed libraries into /vendors. Since you're managing this library, put it into /src.
Secondly, when using classes that aren't namespace (i.e., are in the root namespace), make sure you reference them properly or else PHP will look in the current namespace (which, in this case, is your controller namespace)
Thirdly, the quick-and-dirty solution is to just properly include the files from the controller:
class DefaultController extends Controller
{
protected function includeSsrsSdk()
{
require_once(
$this->container->getParameter( 'kernel.root_dir' )
. '/../src/ssrs/lib/Ssrs/src/SSRSReport.php'
);
}
public function viewAction()
{
$this->includeSsrsSdk();
define("UID", "xxxxxxxx");
define("PASWD", "xxxxxxxx");
define("SERVICE_URL", "http://xxx.xxx.xxx.xxx/ReportServer/");
$report = new \SSRSReport(new \Credentials(UID, PASWD), SERVICE_URL);
return $this->render('myBundle:Default:view.html.twig'
, array('report' => $report)
);
}
}
But that locks your logic for including the library into this one controller. You could make a separate wrapper for the SDK that does this, or even register it as a service.
You are probably using composer with symfony, so this is my suggestion.
Instead of require_once, you should use composer's autoload mechanism for autoloading non namespaced libraries or functions http://getcomposer.org/doc/04-schema.md#files
So just update the autoload section in composer.json.
"autoload": {
"psr-0": { "": "src/" },
"files": ["src/SsrsReport/SSRSReport.php"]
},
For consuming the service I would either use a Facade (extends SSRSREport class) or a Factory which returns it.
Related
I'm new to CI and am struggling to get my head around it.
I'm familiar with Laravel and Symfony and I'm finding it very difficult to test CI code.
I'm considering the service locator pattern to try and work around the dependency injection limitations, but right now I'm struggling with autoloading.
Let's say that I have a model like this:
<?php
class FooModel extends CI_Model
{
public function __construct()
{
parent::__construct();
$this->load->library('alertnotificationservice');
}
}
I want to write a test that looks like this:
<?php
namespace Test\AlertNotification;
// extends TestCase and offers reflection helper methods
use Test\TestBase;
class FooModelTest extends TestBase
{
public function test_my_method()
{
$objectUnderTest = new \FooModel();
}
}
When I run my test I get the error Error: Class 'CI_Model' not found.
I'm using CodeIgnitor 3.1.2 and it doesn't use composer or include the phpunit.xml.dist file that the version 4 manual refers to.
What is the "proper" way to get the autoloading to happen so that I can run my tests?
I haven't found a satisfactory way to do this. I ended up creating a bootstrap.php file that I include from phpunit.xml.dist
It looks something like this:
<?php
require(__DIR__ . '/../vendor/autoload.php');
// this is a copy of the default index.php shipped with CodeIgnitor
// The system and application_folder variables are replaced
// Also, in this version we do not bootstrap the framework and rather include our own version below
require('loader.php');
// this is a modified version of system/core/CodeIgniter.php
// they do bootstrapping, routing, and dispatching in one place
// so we can't use the whole file because dispatching fails when running tests
require('framework.php');
// set up the test environment database connection
putenv('DATABASE_HOST=localhost');
putenv('DATABASE_USER=user');
putenv('DATABASE_PASSWORD=password');
putenv('DATABASE=control_panel');
// CI uses a singleton approach and creates an instance of a child of this class during dispatch
// We need to make sure that the singleton holder is populated
$controller = new CI_Controller();
The framework.php is a stripped down version of that CodeIgnitor file where I've removed the routing and dispatch logic. I've put the files up as a Gist
The crux of the loading in CI seems to exist in system/core/Controller.php and the controller is intended to be something that lets "so that CI can run as one big super object". The load_class function (declared in system/core/Common.php) is responsible for hunting down and loading the class files.
I should also include my composer.json file. I'm using this for testing (CI 3.1.12 does not use composer)
{
"require": {
"guzzlehttp/guzzle": "^6.5"
},
"require-dev": {
"phpunit/phpunit": "^9.1"
},
"autoload": {
"psr-4": {
"Test\\": "tests/"
},
"classmap": ["application/", "system/"]
}
}
I would really like to avoid loading absolutely everything, and would like to be able to mock out bits and pieces, but I'm not optimistic that CodeIgnitor lends itself to this.
Anyway, this approach at least lets me boot my app. There has to be a better way of doing this, I can't believe that the framework could be so popular if it's so hard to test properly.
So subject is the question. Yes, I've searched this forum and googled too. All I've got - useless Symfony docs and casts, some general advises, cli-s and nothing case specific. Maybe yahoo or duckduck could help better?
Everyone is talking about bundles, about how it is important to create them, probably because under the hood Symfony is pushing users away from custom libraries, but no one is actually explains how to start using a bundle - how to start calling its methods.
No, my library is not a composer or whatever package. No, library methods do not return Response objects. No, I am not dealing with composer or recompilations or cli (I use Composercat). No, I will not put library to github or packagist to load it via composer or whatever because it is private library.
Sorry about emotional off-topic.
About the case: I've put my library into the folder
src/lib/MyLibrary.php
I suspect that library class is autoloaded, because if I do not extend Controller with it (if I declare class MyLibrary instead of class MyLibrary extends Controller) - Symfony spits "class name in use" error.
So question: in my controller how to call library method?
$this->get('MyLibrary') doesn't work.
echo print_r($this) doesn't show MyLibrary in this registry too.
Looks like library file is loaded but not registered and/or instantiated. If it is so, then where to point Symfony to register it?
So most of this question is really about how php manages classes. Not so much about Symfony. But that's okay.
To start with it would be best to move project/src/lib to just project/lib. Symfony has some scanning stuff going on in the src directory and you really don't want to have your library mixed up in it.
So:
# lib/MyLibrary.php
namespace Roman;
class MyLibrary
{
public function hello()
{
return 'hello';
}
}
Notice that I added a namespace (Roman) just to distinguish your code from Symfony's.
Now you need to tweak composer.json in order to allow php to autoload your classes:
"autoload": {
"psr-4": {
"App\\": "src/",
"Roman\\": "lib/"
}
},
After adding the Roman line, run "composer dump-autoload" to regenerate the autoload files.
After that, it's just a question of using regular php inside of your application:
# src/Controller/DefaultController.php
namespace App\Controller;
use Roman\MyLibrary;
use Symfony\Component\HttpFoundation\Response;
class DefaultController
{
public function index()
{
$myLibrary = new MyLibrary();
$hello = $myLibrary->hello();
return new Response($hello);
}
}
And that should get your started.
I am trying to change an autoloading system that I've written before.
I'm using composer and at the moment I'm autoloading just one library with class map.
"autoload": {
"classmap": ["libs/"]
}
I want to add a psr-4 loader for the rest of the files and to be able to call the files under libs without namespaces and without "use" them' kind of like aliases in laravel. This is what I'm trying to do:
"autoload": {
"classmap": ["libs/"],
"psr-4": {
"App\\": ""
}
}
So eventually if in "libs" I have the Session class I'm calling it as:
Session::get('anything')
but now after trying to add the psr-4 and calling it from within a namespaced class
namespace App\models;
Class User{
function get(){
return Session::get('anything');
}
}
It won't work anymore because it looks for session within the user's namespace.
I know there are many frameworks which implements it out of the box with aliases.., it's just that this project is kinda old and I'm trying to organize it a bit and get rid of all the requires anywhere - at the moment each model has to be required.
I want to add a psr-4 loader for the rest of the files and to be able to call the files under libs without namespaces and without "use" them' kind of like aliases in laravel.
You can't use the classes without either adding use or adding the fully qualified namespace path starting with the backslash \. This has nothing to do with the way you load these classes, but is a basic requirement of PHP itself - so there is no way around it no matter how you'd like to design your autoloading.
As was commented, adding a backslash works, but this is the required minimum:
namespace App\models;
Class User{
function get(){
return \Session::get('anything');
}
}
I've ended up writing another class that aliasing any classses that i want so i will be able to call them out of the box.
you can see it here:
https://github.com/shahafan/SAmvc-App/blob/master/Config/Aliases.php
basically i'm just using the php class_alias function so i can load all the classes that i want before using them.
i think laravel does it the same way.
I'm setting up a factory to load my classes and their methods for use across my application. Probably continuing on with the strategy method. I feel as though I'm missing a step with my factory, as it throws an exception to finding the class through class_exists(), meanwhile it loads correctly if I omit the check. So, the question is - how do I load my custom factory and classes in laravel appropriately?
Interestingly enough, it doesn't throw an error inside my tests right now.
Only inside my application itself.
I'm newish to the implementation process so I suspect I'm not loading my custom classes appropriately or have a namespacing problem, or maybe should be loading this up in a service provider? Ok that's two questions really. Here's the code:
Factory
namespace app\Support\Humans;
class PeopleFactory
{
public static function build($person)
{
//adding this made it work for my test
$person = __NAMESPACE__ . "\\" . $person;
if (class_exists($person)) {
return new $person();
} else {
//person doesn't exist
throw new Exception;
}
}
}
Person Type
namespace app\Support\Humans;
class PersonType extends BasePerson
{
public function joinHumanity()
{
//etc
}
}
As I'm suspecting a namespacing issue, a segment of my composer.json pointing to the directory of my custom classes.
"autoload": {
"classmap": [
"app/Humans/"
],
"psr-4": {
"Humanity\\": "app/"
}
},
Implementation both in testing or elsewhere
use app\Support\Humans\PeopleFactory;
....
$new_human = PeopleFactory::build($this->user->personType);
$new_human->joinHumanity();
I was just going through the laravel documentation HERE and came across the following peice of code ::
<?php
namespace App\Providers;
use Riak\Connection;
use Illuminate\Support\ServiceProvider;
class RiakServiceProvider extends ServiceProvider
{
/**
* Register bindings in the container.
*
* #return void
*/
public function register()
{
$this->app->singleton('Riak\Contracts\Connection', function ($app) {
return new Connection(config('riak'));
});
}
}
I am new to use in php , just learnt how it functions a few days ago , now does use , when used with a framework like laravel, where one class can be in a directory totally different from another , need to specify the directory structure too ?
I.E. can directory structure impact the way use is used ?
The use statement in PHP (when used outside of a class) is used to import a class from another namespace. Namespaces and folder structure do not necessarily correspond, but it is generally pretty close.
The autoloader used by Laravel, and most other modern PHP applications is part of the Composer package manager. Composer in turn supports multiple namespace standards, most notably PSR-0 and its successor, PSR-4.
In a composer.json file, you'll generally specify a namespace to autoload, and a base directory like so:
{
"autoload": {
"psr-4": {
"My\\Namespace\\": "src"
}
}
}
Any classes in the src/ directory should be in the My\Namespace directory. Classes in src/Model should have the namespace My\Namespace\Model and so on.
What a lot of PHP libraries use nowadays is called an autoloader which may mirror the directory structure, but does not necessarily have to.