Trouble executing the Yii web service demo - php

I'm fairly new to Yii and am currently trying to use this framework to create some PHP web services. While trying to execute the brief tutorial on web services provided on the Yii web site http://www.yiiframework.com/doc/guide/1.1/en/topics.webservice#declaring-web-service-action I ran into some trouble. Namely, I get a "Maximum execution time of 60 seconds exceeded" fatal error when executing the script. My guess is that the getPrice() method actually never gets called.
I'd appreciate any suggestions related to why this may be happening. The contents of my index.php file are listed below. (Note that the Yii gramework is properly installed and I'm running PHP 5.3.0 with the php_soap extension).
<?php
$yii=dirname(__FILE__).'/../yii/framework/yii.php';
defined('YII_DEBUG') or define('YII_DEBUG',true);
defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL',3);
require_once($yii);
class StockController extends CController{
function __construct(){
parent::__construct($this->id, $this->module);
}
public function actions(){
return array(
'quote'=>array(
'class'=>'CWebServiceAction',
),
);
}
/**
* #param string the symbol of the stock
* #return float the stock price
* #soap
*/
public function getPrice($symbol){
$prices=array('IBM'=>100, 'GOOGLE'=>350);
return isset($prices[$symbol])?$prices[$symbol]:0;
//...return stock price for $symbol
}
}
$client=new SoapClient('http://localhost/SampleWebService/?r=stock/quote');
echo $client->getPrice('GOOGLE');
?>

It seems strange that you are declaring the call in the index.php entry script... I'm also not sure why you are overriding the constructor?
And I think, if this really in your entry script, you are missing call the create the application, either Yii::createWebApplication($config)->run(); or Yii::createConsoleApplication($config)->run();, depending on if you are running this as a web or console application.
Have you made sure that the application is running as expected without the SOAP/services stuff? I would set up a basic Hello World app (web or console), then try the web services.

Related

Shopware 6 Production Unable to access plugin services from the container in a unit test

I setup a Unit Test in a Shopware custom (static) Plugin following this guide:
Shopware documentation
Everything runs fine and I'm able to run a unit test
class ProductReturnsTest extends TestCase
{
use IntegrationTestBehaviour;
use StorefrontPageTestBehaviour;
public function testConfirmPageSubscriber(): void
{
$container = $this->getKernel()->getContainer();
$dd = $container->get(CustomDataService::class); <== IT BREAKS HERE ServiceNotFoundException: You have requested a non-existent service
$dd = $container->get('event_dispatcher'); // WORKS WITH SHOPWARE ALIASES NOT WITH PLUGINS
}
}
I can make container->get on any shopware alias but as soon I try to recall and get from the container any service decleared in any xml of any 3th party plugin, i get
ServiceNotFoundException: You have requested a non-existent service "blabla"
What is wrong ?
Take a look at the answer given here: https://stackoverflow.com/a/70171394/10064036.
Probably your plugin is not marked as active in the DB your tests run against.
The test environment has a mostly unpopulated database to allow tests to to run unaffected with their own fixtures only. Therefore after each test there should be a rollback to all transactions made within the test. This principle also includes plugin installations and database transactions they may execute in their lifecycle events.
You may want to install your plugin properly before your tests, so you get a representative state of the environment with the plugins lifecycle events getting dispatched and thereby caused possible changes.
public function setUp(): void
{
$this->installPlugin();
}
private function installPlugin(): void
{
$application = new Application($this->getKernel());
$installCommand = $application->find('plugin:install');
$args = [
'--activate' => true,
'--reinstall' => false,
'plugins' => ['YourPluginName'],
];
$installCommand->run(new ArrayInput($args, $installCommand->getDefinition()), new NullOutput());
}

Testing Laravel Service Providers

I'm (we're) creating a package that acts as a core component for our future CMS and of course that package needs some unit tests.
When the package registeres, the first thing it does is set the back/frontend context like this:
class FoundationServiceProvider extends ServiceProvider
{
// ... stuff ...
public function register()
{
// Switch the context.
// Url's containing '/admin' will get the backend context
// all other urls will get the frontend context.
$this->app['build.context'] = request()->segment(1) === 'admin'
? Context::BACKEND
: Context::FRONTEND;
}
}
So when I visit the /admin url, the app('build.context') variable will be set to backend otherwise it will be set to `frontend.
To test this I've created the following test:
class ServiceProviderTest extends \TestCase
{
public function test_that_we_get_the_backend_context()
{
$this->visit('admin');
$this->assertEquals(Context::BACKEND, app('build.context'));
}
}
When I'm running the code in the browser (navigating to /admin) the context will get picked up and calling app('build.context') will return backend, but when running this test, I always get 'frontend'.
Is there something I did not notice or some incorrect code while using phpunit?
Thanks in advance
Well, this is a tricky situation. As I understand it, laravel initiates two instances of the framework when running tests - one that is running the tests and another that is being manipulated through instructions. You can see it in tests/TestCase.php file.
So in your case you are manipulating one instance, but checking the context of another (the one that did not visit /admin and is just running the tests). I don't know if there's a way to access the manipulated instance directly - there's nothing helpful in documentation on this issue.
One workaround would be to create a route just for testing purposes, something like /admin/test_context, which would output the current context, and the check it with
$this->visit('admin/test_context')->see(Context::BACKEND);
Not too elegant, but that should work. Otherwise, look around in laravel, maybe you will find some undocumented feature.

yii console command custom help information

I created file commands/TestCommand.php in my yii-powered project:
class TestCommand extends CConsoleCommand
{
public function actionIndex()
{
echo "Hello World!\n";
}
}
And it's became visible via yiic:
Yii command runner (based on Yii v1.1.14)
Usage: yiic.php <command-name> [parameters...]
The following commands are available:
- message
- migrate
- shell
- test <<<
- webapp
To see individual command help, use the following:
yiic.php help <command-name>
If I am trying to get some help information about this console command:
php yiic.php help test
I see default text:
Usage: yiic.php test index
How can I write my TestCommand class which will show my help information? Is it a some public field or special method which return help text? I need something like that:
php yiic.php help webapp
Result like I need:
USAGE
yiic webapp <app-path> [<vcs>]
DESCRIPTION
This command generates an Yii Web Application at the specified location.
PARAMETERS
* app-path: required, the directory where the new application will be created.
If the directory does not exist, it will be created. After the application
is created, please make sure the directory can be accessed by Web users.
* vcs: optional, version control system you're going to use in the new project.
Application generator will create all needed files to the specified VCS
(such as .gitignore, .gitkeep, etc.). Possible values: git, hg. Do not
use this argument if you're going to create VCS files yourself.
You can override the default getHelp method to implements your help!
It must return a string that is the help text.
Provides the command description. This method may be overridden to return the actual command description.
Here is the default method:
public function getHelp()
{
$help='Usage: '.$this->getCommandRunner()->getScriptName().' '.$this->getName();
$options=$this->getOptionHelp();
if(empty($options))
return $help."\n";
if(count($options)===1)
return $help.' '.$options[0]."\n";
$help.=" <action>\nActions:\n";
foreach($options as $option)
$help.=' '.$option."\n";
return $help;
}
You can also override the default getOptionHelp method that is called in getHelp
Provides the command option help information. The default implementation will return all available actions together with their corresponding option information.
Source

Is there any way to instantiate CodeIgniter and call a function inside a controller from an outside class?

I am creating a cron job that will run every few minutes and it will read from database data to see which function needs to be called and act accordingly to the data but half the crons are written in codeigniter and the other half in native php.
Is there any way to do this? I have been googling but the anwser. I came up with is that it is impossible. I tried changing directory and than including or requiring index.php from the codeigniter, in which the function is that i need to call.
while doing this if my class is written in native php, It returns some errors that don't make sense and if I correct those errors, I would say that half the system function from codeigniter would be gone. It would still be a question if it will work even then.
If my class is written in codeigniter, when I include index.php, it just breaks. No errors no response or it says that the "ENVIRONMENT" variables are already defined. Thus I have been looking for a way to undefine those variables from config file or overwrite them to null or empty string but nothing works.
If you have any ideas it would be much appreciated.
I saw a question question link about some cron jobs in php where the user #michal kralik gave an answer about what i am doing in general with database and one cron class that will call other crons (coould use help for that too).
btw forgot to mention that using curl and exec will not work, because on our servers they sometimes just stop working for no reason.
UPDATE 1:
this is my class currently after so many tries:
class Unicron extends MY_Controller {
public $config;
function __construct() {
parent::__construct();
}
public function init(){
$config['base_url'] = "http://localhost/test";
define('EXT_CALL', true); // Added EXT_CALL constant to mark external calls
$_GET['controller/method'] = ''; // add pair to $_GET with call route as key
$current = getcwd(); // Save current directory path
chdir('C:/inetpub/wwwroot/test/'); // change directory to CI_INSTALLATION
include_once 'index.php'; // Add index.php (CI) to script
define('BASEPATH', 'C:/inetpub/wwwroot/test/system/');
$this->load->library("../../application/controllers/controller.php");
$job = new $this->controller->Class();
$isDone = $job->exportExcel(somekey);
echo $isDone;
$CI =& get_instance(); // Get instance of CI
$CI->exportExcel('baseparts', 'exportExcel');
// FOR STATIC CALLING!!
//$CI->method('controller','method');
//replace controller and method with call route
// eg: $CI->list('welcome','list'); If calling welcome/list route.
//$OUT->_display(); // to display output. (quick one)
// Or if you need output in variable,
//$output = $CI->load->view('VIEW_NAME',array(),TRUE);
//To call any specific view file (bit slow)
// You can pass variables in array. View file will pick those as it works in CI
chdir($current); // Change back to current directory
echo $current;
}
where i try to define BASEPATH it does not define it nor override the previous value.
In the index.php of other codeigniter i put:
if(!defined('ENVIRONMENT'))
define('ENVIRONMENT', 'development');
this way i resolved my issue with ENVIRONMENT already being set error.
this is a few things i found and combined together, hoping it could work but still when i call it via command line it shows nothing (even tried echo anything everywhere and nothing).
This may be a long comment rather than a answer as the code supplied requires a lot of work to make it useful.
Running multiple instances of 'codeigniter' - executed from codeigniter.
Using the 'execute programs via the shell' from PHP. Each instance runs in its own environment.
There are some excellent answers already available:
By default the PHP commands 'shell' wait for the command to complete...
732832/php-exec-vs-system-vs-passthru.
However, we want to 'fire and forget' quite often so this answer is quite useful...
1019867/is-there-a-way-to-use-shell-exec-without-waiting-for-the-command-to-complete
All i did was use this show an example of how to use 'codeigniter' to do this. The example was the 'Hello World' cli example from user manual. The version of ci is 2.1.14. I haven't used 'ci' before.
It is tested and works on 'PHP 5.3.18' on windows xp.
As well as the usual 'Hello World' example, i used an example of a a command that uses 'sleep' for a total of 20 seconds so that we can easily see that the 'ci' instances are separate from each other while executing.
Examples:
<?php
class Tools extends CI_Controller {
// the usual 'hello world' program
public function message($to = 'World')
{
echo "Hello {$to}!".PHP_EOL;
}
// so you can see that the processes are independant and 'standalone'
// run for 20 seconds and show progress every second.
public function waitMessage($to = 'World')
{
$countDown = 20;
while ($countDown >= 0) {
echo "Hello {$to}! - ending in {$countDown} seconds".PHP_EOL;
sleep(1);
$countDown--;
}
}
}
'ci' code to run 'ci' code...
<?php
class Runtools extends CI_Controller {
/*
* Executing background processes from PHP on Windows
* http://www.somacon.com/p395.php
*/
// spawn a process and do not wait for it to complete
public function runci_nowait($controller, $method, $param)
{
$runit = "php index.php {$controller} {$method} {$param}" ;
pclose(popen("start \"{$controller} {$method}\" {$runit}", "r"));
return;
}
// spawn a process and wait for the output.
public function runci_wait($controller, $method, $param)
{
$runit = "php index.php {$controller} {$method} {$param}";
$output = exec("{$runit}");
echo $output;
}
}
How to run them from the cli...
To run the 'ci' 'nowait' routine then do:
php index.php runtools runci_nowait <controller> <method> <param>
where the parameters are the ci controller you want to run. Chnge to 'runci_wait' for the other one.
'Hello World: 'wait for output' - (ci: tools message )
codeigniter>php index.php runtools runci_wait tools message ryan3
Hello ryan3!
The waitMessage - 'do not wait for output' - (ci : tools waitMessage )
codeigniter>php index.php runtools runci_nowait tools waitMessage ryan1
codeigniter>php index.php runtools runci_nowait tools waitMessage ryan2
These will start and run two separate 'ci' processes.

Create cronjob with Zend Framework

I am trying to write a cronjob controller, so I can call one website and have all modules cronjob.php executed. Now my problem is how do I do that?
Would curl be an option, so I also can count the errors and successes?
[Update]
I guess I have not explained it enough.
What I want to do is have one file which I can call like from http://server/cronjob and then make it execute every /application/modules/*/controller/CronjobController.php or have another way of doing it so all the cronjobs aren't at one place but at the same place the module is located. This would offer me the advantage, that if a module does not exist it does not try to run its cronjob.
Now my question is how would you execute all the modules CronjobController or would you do it a completly different way so it still stays modular?
And I want to be able to giveout how many cronjobs ran successfully and how many didn't
After some research and a lot procrastination I came to the simple conclusion that a ZF-ized cron script should contain all the functionality of you zend framework app - without all the view stuff. I accomplished this by creating a new cronjobfoo.php file in my application directory. Then I took the bare minimum from:
-my front controller (index.php)
-my bootstrap.php
I took out all the view stuff and focused on keeping the environment setup, db setup, autoloader, & registry setup. I had to take a little time to correct the document root variable and remove some of the OO functionality copied from my bootstrap.
After that I just coded away.. in my case it was compiling and emailing out nightly reports. It was great to use Zend_Mail. When I was confident that my script was working the way I wanted, I just added it my crontab.
good luck!
For Zend Framework I am currently using the code outlined bellow. The script only includes the portal file index.php, where all the paths, environment and other Zendy code is bootstrapped. By defining a constant in the cron script we cancel the final step , where the application is run.
This means the application is only setup, not even bootstrapped. At this point we start bootstraping the resources we need and that is that
//public/index.php
if(!defined('DONT_RUN_APP') || DONT_RUN_APP == false) {
$application->bootstrap()->run();
}
// application/../cron/cronjob.php
define("DONT_RUN_APP",true);
require(realpath('/srv/www/project/public/index.php'));
$application->bootstrap('config');
$application->bootstrap('db');
//cron code follows
I would caution putting your cronjobs accessible to the public because they could be triggered outside their normal times and, depending on what they do, cause problems (I know that is not what you intend, but by putting them into an actual controller it becomes reachable from the browser). For example, I have one cron that sends e-mails. I would be spammed constantly if someone found the cron URL and just began hitting it.
What I did was make a cron folder and in there created a heartbeat.php which bootstraps Zend Framework (minus MVC) for me. It checks a database which has a list of all the installed cron jobs and, if it is time for them to run, generates an instances of the cron job's class and runs it.
The cron jobs are just child classes from an abstract cron class that has methods like install(), run(), deactivate(), etc.
To fire off my job I just have a simple crontab entry that runs every 5 minutes that hits heartbeat.php. So far it's worked wonderful on two different sites.
Someone mentioned this blog entry a couple days ago on fw-general (a mailinglist which I recommend reading when you use the Zend Framework).
There is also a proposal for Zend_Controller_Request_Cli, which should address this sooner or later.
I have access to a dedicated server and I initially had a different bootstrap for the cron jobs. I eventually hated the idea, just wishing I could do this within the existing MVC setup and not have to bother about moving things around.
I created a file cron.sh, saved is within my site root (not public) and in it I put a series of commands I would like to run. As I wanted to run many commands at once I wrote the PHP within my controllers as usual and added curl calls to those urls within cron.sh. for example curl http://www.mysite.com/cron_controller/action Then on the cron interface I ran bash /path/to/cron.sh.
As pointed out by others your crons can be fired by anyone who guesses the url so there's always that caveat. You can find a solution to that in many different ways.
Take a look at zf-cli:
scripts at master from padraic/ZFPlanet - GitHub
This handles well all cron jobs.
Why not just create a crontab.php, including, or requiring the index.php bootstrap file?
Considering that the bootstrap is executing Zend_Loader::registerAutoload(), you can start working directly with the modules, for instance, myModules_MyClass::doSomething();
That way you are skipping the controllers. The Controller job is to control the access via http. In this case, you don't need the controller approach because you are accessing locally.
Do you have filesystem access to the modules' directories? You could iterate over the directories and determine where a CronjobController.php is available. Then you could either use Zend_Http_Client to access the controller via HTTP or use an approach like Zend_Test_PHPUnit: simulate the actual dispatch process locally.
You could set up a database table to hold references to the cronjob scripts (in your modules), then use a exec command with a return value on pass/fail.
I extended gregor answer with this post. This is what came out:
//public/index.php
// Run application, only if not started from command line (cli)
if (php_sapi_name() != 'cli' || !empty($_SERVER['REMOTE_ADDR'])) {
$application->run();
}
Thanks gregor!
My solution:
curl /cron
Global cron method will include_once all controllers
Check whether each of the controllors has ->cron method
If they have, run those.
Public cron url (for curl) is not a problem, there are many ways to avoid abuse. As said, checking remote IP is the easiest.
This is my way to run Cron Jobs with Zend Framework
In Bootstrap I will keep environment setup as it is minus MVC:
public static function setupEnvironment()
{
...
self::setupFrontController();
self::setupDatabase();
self::setupRoutes();
...
if (PHP_SAPI !== 'cli') {
self::setupView();
self::setupDbCaches();
}
...
}
Also in Bootstrap, I will modify setupRoutes and add a custom route:
public function setupRoutes()
{
...
if (PHP_SAPI == 'cli') {
self::$frontController->setRouter(new App_Router_Cli());
self::$frontController->setRequest(new Zend_Controller_Request_Http());
}
}
App_Router_Cli is a new router type which determines the controller, action, and optional parameters based on this type of request: script.php controller=mail action=send. I found this new router here: Setting up Cron with Zend Framework 1.11
:
class App_Router_Cli extends Zend_Controller_Router_Abstract
{
public function route (Zend_Controller_Request_Abstract $dispatcher)
{
$getopt = new Zend_Console_Getopt (array());
$arguments = $getopt->getRemainingArgs();
$controller = "";
$action = "";
$params = array();
if ($arguments) {
foreach($arguments as $index => $command) {
$details = explode("=", $command);
if($details[0] == "controller") {
$controller = $details[1];
} else if($details[0] == "action") {
$action = $details[1];
} else {
$params[$details[0]] = $details[1];
}
}
if($action == "" || $controller == "") {
die("Missing Controller and Action Arguments == You should have:
php script.php controller=[controllername] action=[action]");
}
$dispatcher->setControllerName($controller);
$dispatcher->setActionName($action);
$dispatcher->setParams($params);
return $dispatcher;
}
echo "Invalid command.\n", exit;
echo "No command given.\n", exit;
}
public function assemble ($userParams, $name = null, $reset = false, $encode = true)
{
throw new Exception("Assemble isnt implemented ", print_r($userParams, true));
}
}
In CronController I do a simple check:
public function sendEmailCliAction()
{
if (PHP_SAPI != 'cli' || !empty($_SERVER['REMOTE_ADDR'])) {
echo "Program cannot be run manually\n";
exit(1);
}
// Each email sent has its status set to 0;
Crontab runs a command of this kind:
* * * * * php /var/www/projectname/public/index.php controller=name action=send-email-cli >> /var/www/projectname/application/data/logs/cron.log
It doesn't make sense to run the bootstrap in the same directory or in cron job folder. I've created a better and easy way to implement the cron job work. Please follow the below things to make your work easy and smart:
Create a cron job folder such as "cron" or "crobjob" etc. whatever you want.
Sometimes we need the cron job to run on a server with different interval like for 1 hr interval or 1-day interval that we can setup on the server.
Create a file in cron job folder like I created an "init.php", Now let's say you want to send a newsletter to users in once per day. You don't need to do the zend code in init.php.
So just set up the curl function in init.php and add the URL of your controller action in that curl function. Because our main purpose is that an action should be called on every day. for example, the URL should be like this:
https://www.example.com/cron/newsletters
So set up this URL in curl function and call this function in init.php in the same file.
In the above link, you can see "cron" is the controller and newsletters is the action where you can do your work, in the same way, don't need to run the bootstrap file etc.

Categories