I'm currently building some tests for my Image model which handles files and wanted to use vsfStream to test the file manipulations.
I can't seem to get my tests to recognise the vsfClass.
The vsfStream library is in app/Vendor/vsfStream
My ImageTest.php file is as follows:
<?php
App::uses('Image', 'Model');
App::uses('vfsStream', 'Vendor');
//App::import('Vendor', 'vfsStream', array('file' => 'vfsStream' . DS . 'vfsStream.php'));
/**
* Image Test Case
*
*/
class ImageTest extends CakeTestCase {
/**
* Fixtures
*
* #var array
*/
public $fixtures = array(
'app.image',
'app.contractor',
'app.project',
'app.project_contractor',
'app.project_image'
);
/**
* setUp method
*
* #return void
*/
public function setUp() {
parent::setUp();
$this->Image = ClassRegistry::init('Image');
$this->root = vfsStream::setup('exampleDir');
}
...more code
I get the error
Error: Class 'vfsStream' not found
File: C:\xampp\htdocs\sgaluminium\app\Test\Case\Model\ImageTest.php
Line: 32
Any help would be appreciated. Thanks
It seems that vsfStream was not loaded correctly. Did you load it with composer? I recommend you use this composer plugin: https://github.com/uzyn/cakephp-composer (read the how-to-use section on their website) and you write your composer.json like this:
{
"require-dev": {
"phpunit\/phpunit": "3.7.35",
"mikey179/vfsStream": "1.2"
}
}
This setup works very well in my project. In my test-file I use:
App::uses('vfsStream', 'Vendor');
just like you do.
Related
I want to do functional tests on my Symfony (5.1) application, this application uses an Active Directory server as a "datas" database (creating , listing , updating datas). I'm using the Symfony ldap component. Code example below may contain typos.
Controller
class DatasController
{
/**
* #Route("/datas", name="datas")
* #IsGranted("ROLE_USER")
*
* #return Response
* #desc Displays LDAP datas
*/
public function datasList(DatasRepository $datasRepository)
{
$datas = $datasRepository->findAll();
return $this->render('datas/list.html.twig', [
'datas' => $datas,
]);
}
}
Repository
class DatasRepository
{
private Ldap $ldap;
private EntryManagerInterface $manager;
/**
* DatasRepository constructor.
* Service injected params
*/
public function __construct(Ldap $ldap, string $ldapAdminLogin, string $ldapAdminPwd)
{
$this->ldap = $ldap->bind($ldapAdminLogin, $ldapAdminPwd);
$this->manager = $ldap->getEntryManager();
}
public function create(Data $data): void
{
// ... some $data to Symfony\Component\Ldap\Entry $entry logic
$this->manager->add( $entry );
}
/**
* #return datas[]
*/
public function findAll()
{
$this->ldap->query('ou=test', '(&(objectclass=person))');
$entries = $query->execute()->toArray();
// ... some $entries to $datas logic
return $datas;
}
}
Test
class DatasControllerTest extends WebTestCase
{
public function testDatasList()
{
$client = static::createClient();
$client->request('GET', '/datas');
# Crash can't contact LDAP and thats logical
$this->assertResponseIsSuccessful();
}
}
So, how to do functional test on "GET /datas" ?
What part of the code should i mock to maximize test efficiency and coverage ?
Some additional information :
I can't have a dedicated LDAP server for tests (tests are run under
Docker via gitlab-ci)
I'm aware of the "don't mock what you don't
own".
I've read many posts/articles saying "you should mock the
LdapAdapter" but i have no idea on how to achieve this and haven't
found any example.
Any suggestion is welcome.
Thanks
Eric
About mockin external services: you can extend test service from the original one and make it methods behave how you want. Ex.:
class TestService extends \Symfony\OrAnyOtherExternalService
{
public function getConnection()
{
return new Connection([]);
}
}
then in your services_test.yaml change the class of this service to you tests service:
services:
Symfony\OrAnyOtherExternalService:
class: TestData\Services\TestService
this way in test environment application will use TestService instead of original
I would like to create a component in yii2 that can be accessed throughout the web application but only create one instance and be able to retrieve that instance wherever needed.
namespace app\components;
use yii;
use yii\base\Object;
class ContentManagerComponent extends Object
{
public function init(){
parent::init();
}
public function toBeUsed (){
return 'some variable';
}
}
Then I want to be able to call the component in other parts of the web application, like in the controllers.
namespace app\Controllers;
use yii;
use app\controllers\
class SomeController extends Controller {
public function actionDoSomething(){
$contentComponent = Yii::$app->content;
$someVariable = $contentComponent->toBeUsed()
return $this->render( 'someView',[
'variable' => $someVariable,
]
}
}
I have also put the component in the web.php file.
$config = [
'components' => [
'content' => [
'class' => 'app\components\ContentManagerComponent',
],
],
],
What I'm ending up with is phpstorm telling me that the class doesn't exist. I would also like to have intelisense like the other components do in the application.
intelisense:
noIntele:
update:#
I was able to get intelisense working by adding the this line as suggested by the answer below. /** #var ContentComponent $contentManager */
However I got tired of always typing that out above each time I wanted to use the Content Component. So I created a function in the base class of the components I was needing Content Component that return the Continent Component using the Yii::app->content method. Above the function that would return the Content Component I added the comment that it would return ContentComponent and the class of the ContentComponent. Now in order for me to use the component with intelisense working. All I would have to do is $this->getContentComponent. Php storm would be able to identify that the content component was of the class returned. Bellow is an example.
class BaseClass extends object
{
/**
* #return ContentComponent
*/
function getContentComponent () {
$contentComponent = Yii::app->content;
return $contentComponent
}
}
class SomeClass extends BaseClass
public function someFunction () {
$contentComponent = $this->getContentComponent;
}
PHPStorm don't recognize your custom component because they are created dynamically on framework load and attached to Yii::$app on runtime, That's why PHPStorm don't recognize your custom components. So until someone will develop an intelligent plugin for IDEs like PHPStorm, you will have to make some tweaks to achieve your goals.
You have 2 options:
Create a new Yii.php file (in root dir) for reference with all the
necessary docs and this will tell PHPStorm in the entire project
about your components (I putted here a full example, if you want to create components which available only for console/web/both) look at * #property ContentManagerComponent $content (More read - credit to samdark Alexander Makarov, one of Yii core contributors):
<?php
use app\components\ContentManagerComponent;
use yii\BaseYii;
/**
* Class Yii
* Yii bootstrap file.
* Used for enhanced IDE code autocompletion.
*/
class Yii extends BaseYii
{
/**
* #var BaseApplication|WebApplication|ConsoleApplication the application instance
*/
public static $app;
}
/**
* Class BaseApplication
* Used for properties that are identical for both WebApplication and ConsoleApplication
*
* #property ContentManagerComponent $content
*/
abstract class BaseApplication extends yii\base\Application
{
}
/**
* Class WebApplication
* Include only Web application related components here
*
*/
class WebApplication extends yii\web\Application
{
}
/**
* Class ConsoleApplication
* Include only Console application related components here
*/
class ConsoleApplication extends yii\console\Application
{
}
Create a PHP doc everywhere you want to use your component which
will tell PHPStorm that your variable is instance of the component:
public function actionDoSomething()
{
/** #var ContentManagerComponent $contentComponent */
$contentComponent = Yii::$app->content;
$someVariable = $contentComponent->toBeUsed();
return $this->render('someView', [
'variable' => $someVariable,
]);
}
As you can see option 1 is a solution provided by one of the core contributors of the Yii framework, so I assumes that this suppose to be the right choice for now (until there will be native support by JetBrains or any plugin)
Once you have declared your content component in you config files
$config = [
'components' => [
'content' => [
'class' => 'app\components\ContentManagerComponent',
],
],
],
then you can refer tor the component using
Yii::$app->content
eg
Yii::$app->content->yourMethod();
eventually add use Yii; or refer using \Yii::$app->content
I use the following method for intellisense.
1.Set your components inside config.
$config = [
'components' => [
'content' => [
'class' => 'app\components\ContentManagerComponent',
],
'content2' => [
'class' => 'app\components\ContentManagerComponent2',
],
],
],
2.Have an AppComponents trait, documenting all instances that your $app has. I like to have it inside components/ directory.
<?php
namespace app\components;
/**
* Trait AppComponents
* #package app\components
*
* #property ContentManagerComponent1 $content
* #property ContentManagerComponent2 $content2
*/
trait AppComponents {}
3.Return the Yii::$app your own way. Trick the editor into believing that AppComponents may be returned.
<?php
namespace app\controllers;
use frontend\components\AppComponents;
use yii\rest\Controller;
class SiteController extends Controller {
/**
* #return \yii\web\Application|AppComponents
*/
public static function app() {
return \Yii::$app;
}
}
Now you can use SiteController::app()->content with intellisense. You can have a nicer Root class, and replace \Yii::$app with Root::app(). All Controllers may inherit from the Root class. You can also use self::app() when coding inside the extended Controllers.
I am trying to use GeoIP2 PHP API ( https://github.com/maxmind/GeoIP2-php ) within CodeIgniter. How can I load GeoIP2 and use it for user geolocation?
I have tried loading it like this:
$this->load->library("GeoIp2/Database/Reader");
or
require APPPATH . "libraries/GeoIp2/ProviderInterface.php";
require APPPATH . "libraries/GeoIp2/Database/Reader.php";
or
$this->load->file("GeoIp2/ProviderInterface");
$this->load->library("GeoIp2/Database/Reader");
I get this error: "Unable to load the requested file: ProviderInterface"
I have looked this Namespace in PHP CodeIgniter Framework , but i have little experience with namespaces.
No success with this, I am not winning, I really do not know how to implement this within CodeIgniter.
I was trying to find the solution of this question. But couldn't find on stackoverflow. I am writing my own code here. Maybe, it will be helpful for someone. I have added a new function in my utility_helper.php file :
function get_ip_country_code($ip_address) {
require APPPATH .'third_party/GeoIP2/autoload.php';
$reader = new GeoIp2\Database\Reader(FCPATH.'public/geoip/GeoIP2-Country.mmdb');
$record = $reader->country($ip_address);
return $record->country->isoCode;
}
I put the GeoIP2 library in the third_party folder and put the mmdb file in the public folder. It works fine for me. I hope it will save someone's time :)
The GeoIp2 php sdk takes advantage of PHP's namespace feature, which the CodeIgniter framework does not support, which is why you're getting the error when you try to load the library. The post you linked to offers a solution using spl_autoload, however I do not use CodeIgniter and haven't tested it with the GeopIp2 php sdk.
Few ways you can embed this within CodeIgniter.
First, you need to include it within the script:
require_once( 'GeoIp2/vendor/autoload.php' );
use GeoIp2\Database\Reader;
Next, I call Reader() for the detection methods
$reader = new Reader('GeoIp2/GeoIP2-City.mmdb');
$record = $reader->city($ip);
// Country (code)
$record->country->isoCode;
// State
$record->mostSpecificSubdivision->name;
// City
$record->city->name;
// zip code
$record->postal->code;
I just tested this on CodeIgniter 3x and works.
I used a bridge class. Inside /application/libraries create a file called CI_GeoIp2.php and add the following code.
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
/**
* GeoIp2 Class
*
* #package CodeIgniter
* #subpackage Libraries
* #category GeoIp2
* #author Timothy Marois <timothymarois#gmail.com>
*/
require_once( APPPATH . 'third_party/GeoIp2/vendor/autoload.php' );
use GeoIp2\Database\Reader;
class CI_GeoIp2 {
protected $record;
protected $database_path = 'third_party/GeoIp2/GeoIP2-City.mmdb';
public function __construct() {
$ci =& get_instance();
$reader = new Reader(APPPATH.$this->database_path);
$ip = $ci->input->ip_address();
if ($ci->input->valid_ip($ip)) {
$this->record = $reader->city($ip);
}
log_message('debug', "CI_GeoIp2 Class Initialized");
}
/**
* getState()
* #return state
*/
public function getState() {
return $this->record->mostSpecificSubdivision->name;;
}
/**
* getState()
* #return country code "US/CA etc"
*/
public function getCountryCode() {
return $this->record->country->isoCode;
}
/**
* getCity()
* #return city name
*/
public function getCity() {
return $this->record->city->name;
}
/**
* getZipCode()
* #return Zip Code (#)
*/
public function getZipCode() {
return $this->record->postal->code;
}
/**
* getRawRecord()
* (if you want to manually extract objects)
*
* #return object of all items
*/
public function getRawRecord() {
return $this->record;
}
}
Now you can either autoload or load it up using
$this->load->library("CI_GeoIp2");
I prefer to autoload it like this under autoload.php config
$autoload['libraries'] = array('CI_GeoIp2'=>'Location');
So within the script I use,
$this->Location->getState()
$this->Location->getCity()
... and so on
In using the laravel framework, how can I call a function defined in base_controller, in a view. For exacmple:
class Base_Controller extends Controller {
public static function format_something()
{
return something;
}
}
How can i call format_something() in a view file?
Usually the error I get looks something like this:
Method [link_to_action] is not defined on the View class.
Probably a silly question, but thanks in advance!
Edit
Okay! First the correct place to do something like this is in the libraries folder.
Second, problem is that your class cannot have underscores.
So in application/libraries I made file AppHelper.php with class
class AppHelper {
public static function format_something()
{
return something;
}
}
And can call it like:
$formated = AppHelper::format_something;
Thanks for the help and the good forum find Boofus McGoofus.
For me is working:
Create directory "helpers" or whatever and file:
// app/helpers/AppHelper.php
class AppHelper {
public static function format_something()
{
return something;
}
}
Add path to composer.json
// composer.json
"autoload": {
"classmap": [
"app/helpers" // <-------- add this line
]
},
Run: (reload the autoload)
composer dump-autoload
Now you can call:
$formated = AppHelper::format_something();
This answer was written for Laravel 3. For Laravel 4 and after, Lajdák Marek's answer using Composer's autoloader is better.
Functions like format_something() don't belong in the controller. The controller should just be about collecting data from various sources and passing it to the view. It's job is mostly just routing.
I've created a folder called "helpers" in the application folder for all my little helpery functions. To make sure all my controllers, views, and models have access to them, I've included the following in my start.php file:
foreach(glob(path('app').'helpers/*.php') as $filename) {
include $filename;
}
I suspect that there's a better way to do that, but so far it has worked for me.
You can inspire yourself from Laravel framework itself.
I will take your example of a formatter and refer to url helper in Laravel Framework.
Start by creating your own helpers.php file:
<?php
if (! function_exists('format_that')) {
/**
* Generate something
*
* #param string $text
* #return string
*/
function format_that($text)
{
return app('formatter')->format_that($text);
}
}
And add it to your composer.json file:
"autoload": {
"files": [
"app/helpers/helpers.php"
]
}
Run this command to recreate the autoload php file:
$ composer dumpautoload
Create your service provider app/Providers/FormatterServiceProvider.php:
<?php
namespace Illuminate\Routing;
use Illuminate\Support\ServiceProvider;
use App\Helpers\FormatGenerator;
class FormatterServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->app['formatter'] = $this->app->share(function ($app) {
return new FormatGenerator($app['request']);
});
}
}
Register your service provider. Laravel framework invokes register method but you only need to add it to your app config file config/app.php:
'providers' => [
/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
// other providers...
App\Providers\FormatterServiceProvider::class,
]
Finally, create your actual generator class app/Helpers/FormatGenerator.php
<?php
namespace App\Helpers;
use Illuminate\Http\Request;
class FormatGenerator
{
protected $request;
/**
* Create a new URL Generator instance.
*
* #param \Illuminate\Routing\RouteCollection $routes
* #param \Illuminate\Http\Request $request
* #return void
*/
public function __construct(Request $request)
{
$this->request = $request;
}
public function format_that($text){
if ($request->path() == "home"){
return mb_strtoupper($text);
}
else{
return $text;
}
}
}
You can optionally create a Facade app/Facade/Formatter.php, to be able to do Formatter::format_that($text):
<?php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
/**
* #see \App\Helpers\FormatGenerator
*/
class Formatter extends Facade
{
protected static function getFacadeAccessor() { return 'formatter'; }
}
You could ask yourself:
Why the facade? You can reuse the component somewhere else by simply calling Formatter::format_that($text) instead of app('formatter')->format_that($text). Sugar syntax really.
Why the Service provider? Dependence injections. If you need to use Request or want to build a complex object, the Service provider will take care of that for you and make it available in your $app object.
I've got a class library in defined here .../projectname/library/Me/Myclass.php defined as follows:
<?php
class Me_Myclass{
}
?>
I've got the following bootstrap:
<?php
/**
* Application bootstrap
*
* #uses Zend_Application_Bootstrap_Bootstrap
*/
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
/**
* Bootstrap autoloader for application resources
*
* #return Zend_Application_Module_Autoloader
*/
protected function _initAutoload()
{
$autoloader = new Zend_Application_Module_Autoloader(array(
'namespace' => 'Default',
'basePath' => dirname(__FILE__),
));
$autoloader->registerNamespace('Me_');
return $autoloader;
}
/**
* Bootstrap the view doctype
*
* #return void
*/
protected function _initDoctype()
{
$this->bootstrap('view');
$view = $this->getResource('view');
$view->doctype('XHTML1_STRICT');
}
/**
* Bootstrap registry and store configuration information
*
* #return void
*/
protected function _initRegistry()
{
$config = new Zend_Config_Ini(APPLICATION_PATH .
'/configs/application.ini', APPLICATION_ENV,
array('allowModifications'=>true));
Zend_Registry::set('configuration', $config);
}
}
In my controller I try to instantiate the class like this:
<?php
class SomeController extends Zend_Controller_Action
{
public function indexAction()
{
$classMaker=new Me_Myclass();
}
}
?>
When I navigate directly to http:/something.com/projectname/some?id=1 I get the following error:
Fatal error: Class 'Me_Myclass' not found in /home/myuser/work/projectname/application/controllers/SomeController.php on line x
Any ideas?
Potentially Pertinent Miscellany:
The autoloader seems to work when I'm extending models with classes I've defined in other folders under application/library.
Someone suggested changing the 'Default', which I attempted but it didn't appear to fix the problem and had the added negative impact of breaking function of models using this namespace.
You class needs to be name Me_Myclass:
class Me_Myclass
{
}
Move your library folder up a level so that you have the folder structure:
/
/application
/library
/public
And then in your Bootstrap add the following to the _initAutoload():
Zend_Loader_Autoloader::getInstance()->registerNamespace('Me_');
you can define the autoload dir in the config.ini file like this:
autoloaderNamespaces[] = "Me_"
;You could add as many as you want Classes dir:
autoloaderNamespaces[] = "Another_"
autoloaderNamespaces[] = "Third_"
works 100%
I think #smack0007 means replace the contents of your _initAutoload method with Zend_Loader_Autoloader::getInstance()->registerNamespace('Me_'); so it looks like this:
protected function _initAutoload()
{
Zend_Loader_Autoloader::getInstance()->registerNamespace('Me_');
}
Not sure if this is your problem, but I just spent the last day and half trying to figure out my own similar problem (first time loading it up on Linux from Windows). Turns out I was blind to my library's folder name case.
/library
/Tlib
is not the same as (on *nix)
/library
/tlib
Class name is typically this
class Tlib_FooMe {
...
}
Hope this helps someone who is similarly absentminded.