I've been playing with the Phalcon framework for some time now, but I haven't managed to figure out how to work with application events (via the EventManager).
Specifically, I would like to create a 'boot' event, so I can run code only once per application instead of once per controller (which would be the case if I extended a ControllerBase).
The manual gives me the syntax to use, but I still don't know how to use it and in what file I should put it.
I know I could just require a 'boot.php' file in index.php, but that isn't really an elegant solution.
Assume that you have the index.php file which is the entry point of your application. In that file you have code to register all the services in your application. My personal preference is to keep the index.php file as small as possible and put the bootstrap sequence (registering services) in a different file.
So I would have in the index.php
<?php
use \Phalcon\DI\FactoryDefault as PhDi;
error_reporting(E_ALL);
date_default_timezone_set('US/Eastern');
if (!defined('ROOT_PATH')) {
define('ROOT_PATH', dirname(dirname(__FILE__)));
}
try {
include ROOT_PATH . "/app/var/bootstrap.php";
/**
* Handle the request
*/
$di = new PhDi();
$app = new Bootstrap($di);
echo $app->run(array());
} catch (\Phalcon\Exception $e) {
echo $e->getMessage();
} catch (PDOException $e){
echo $e->getMessage();
}
The bootstrap.php contains the functions needed to initialize all the services that your application needs. One of these services can be as follows:
public function initEventsManager($app)
{
$evManager = new \Phalcon\Events\Manager();
$app->setEventsManager($evManager);
$evManager->attach(
"application",
function($event, $application) {
switch ($event->getType()) {
case 'boot':
$this->handleBoot();
break;
case 'beforeStartModule':
$this->handleBeforeStartModule();
break;
case 'afterStartModule':
$this->handleAfterStartModule();
break;
case 'beforeHandleRequest':
$this->handleBeforeHandleRequest();
break;
case 'afterHandleRequest':
$this->handleAfterHandleRequest();
break;
}
);
}
In your bootstrap.php file you should have relevant functions such as handleBoot handleAfterHandleRequest etc. that will process the code the way you need to.
The above function to register the events manager will need to be invoked with the Phalcon\Mvc\Application as a parameter. A good place to put it would be here (based on the examples)
https://github.com/phalcon/website/blob/master/app/var/bootstrap.php#L62
Related
I am testing out the Gitonomy Library within PHP and something I just cannot put my finger on. I have created a small application found here with a class that uses the Repository class from the library to list available branches on GITHUB Remote REposictory. Below is the class:
namespace LuSoft\Library{
use Gitonomy\Git\Repository as RemoteRepositroy;
class Repository
{
public function getBranches($repository)
{
if(!is_string($repository)){
throw new Exception("Reposotory must be in a form of a string.");
}
$rr = new RemoteRepositroy($repository);
$branches = [];
foreach($rr->getReferences()->getBranches() as $branch){
$branches[] = $branch->getName();
}
$rr->run('fetch', array('--all'));
return $branches;
}
}
}
I have followed the example from here. Below is how I call this class in my index.php page.
require_once "vendor/autoload.php";
use LuSoft\Library\Repository;
$repository = new Repository();
try{
var_dump($repository->getBranches(__DIR__));
}catch(\Exception $e){
echo "Exception :" . $e->getMessage();
}
Below is the error that I get:
Exception :Error while getting list of references: '"git"' is not
recognized as an internal or external command, operable program or
batch file.
I am using the application I am testing with to get the branches and if things went on correctly I am suppose to be seeing master.
Can someone please explain to me how does the library really work in simplest form?
NB: My index.php is on the same folder level of the application where git has been initialized.
We have a webservice written in php. It's pretty simple, you do a http request with a certain "action" (post variable) and the webservice executes the function accordingly. This webservice is used by our apps (android and iOS).
We also have a user management system. It started as a create/edit/delete users page, but has since grown a lot to include more features. We would like to use some of the webservice's functionality from within this php application.
This is how the webservice was designed:
include "config.php"; //for database connection
//some more includes for additional classes and functions
$action = $_POST["action"];
switch ($action) {
case "login": {
//do login stuff
break;
}
case "get_roster": {
//do roster list stuff
break;
}
}
This does not work when including the file from another php file however, so I put it in a function (so it doesn't execute immediately). To distinguish between a http call and an include I added a $userId variable. If it is not set the function is called immediately, if it is set the function should be called from the file where the webservice was included. This works perfectly, but it makes my eye twitch to see this code. I would really like a more elegant solution, but I'm not sure if it's possible. This is how it is coded now:
include "config.php"; //for database connection
//some more includes for additional classes and functions
//there is no way to set $userId when doing a http request
//when including this file you can set $userId first so startService() isn't called immediately
if (!isset($userId)) {
startService();
}
function startService() {
$action = $_POST["action"];
switch ($action) {
case "login": {
//do login stuff
break;
}
case "get_roster": {
//do roster list stuff
break;
}
}
}
What is the best way to achieve this behaviour? What I want is that the switch case is executed immediately when doing a http request, but not immediately when just including this file from another php file.
It's pretty trivial, really: separate the function declaration and the function invocation into two separate files.
services.php
function startService($action) {
...
}
go.php
require_once 'services.php';
startService($_POST['action']);
If you keep up this attitude of logical separation between code declaration and code invocation and additionally inject arguments as shown above, you're making your code a whole lot more flexible, reusable and maintainable. What you have here is essentially the humble beginnings of a proper controller in MVC terms.
So my project uses an MVC framework and I have a page with an Ajax script I run to get content from the server. When the PHP script is called in the Ajax script, I want to access the classes already in my library for use in the PHP script. To do this, I use what I call an ajaxBootstrap to call the appropriate function that then instantiates the objects needed for that specific Ajax script.
To load those classes from my library I have an autoload function in my ajaxBootstrap so I don't need to use a bunch of require and include statements. My problem is those files aren't being loaded due to a path issue with the autoload function. When I use a require statement with the same path, the classes load with no problems, its only when I try to load them using the autoload function that I get an 500 internal server error.
Here is my ajaxBootstrap file:
// This file routes Ajax requests made in JS files and instantiates a specific object to carry out the actions needed for that particular Ajax operation
// Autoload any classes that are required
function autoLoad($classToLoad)
{
if(file_exists('../library/' . $classToLoad . 'class.php')) // File in the library folder
{
require('../library/' . $classToLoad . '.class.php');
}
else if(file_exists('../../app/models/' . $classToLoad . 'class.php')) // File in the models folder
{
require('../../app/models/' . $classToLoad . '.class.php');
}
}
spl_autoload_register('autoLoad');
// Determine which function to call based on the url that's listed in the Ajax request
switch($_GET['action'])
{
case 'pageOne':
pageOne();
break;
case 'pageTwo':
pageTwo();
break;
}
function pageOne()
{
$test = new Test();
$test->funcThatReturnStuff();
}
function pageTwo()
{
$test2 = new Test2();
$test2->funcThatReturnStuff();
}
Like I mentioned eariler, if I use a require statement such as:
require('../library/Test.class.php');
$test = new Test();
$test->funcThatReturnStuff();
The class loads and works just fine. But using the same path in the autoloader function throws an error. The really odd thing is if I put an else if statement in the autoloader that loads a class from the folder where my ajaxBootstrap is it also works fine too...
I know I could just use the require statements and be done with the problem but I want to be able to scale the project and not need to use loads of require statements in the future. BTW, I use '../' to get from where my ajaxBootstrap file is to my other folders.
Also, to add to my previous post, I've tried replacing the ../ with absolute paths using define('ROOT', dirname(__FILE__) . '/') and also define('ROOT', $_SERVER['DOCUMENT_ROOT'] . '/path/to/folder/') neither of which worked and still gave me the internal server error in Firebug. In addition, I haven't received any errors in my error log either.
Nevermind... Even after staring at my code for the past few hours I somehow missed the missing period in two of my file paths. I hate coding sometimes... Thank you to anyone who took time to read this.
In the Magento Ecommerce System, there are three events that fire before the system is fully bootstrapped
resource_get_tablename
core_collection_abstract_load_before
core_collection_abstract_load_after
These events also fire after Magento has bootstrapped.
What's a safe and elegant (and maybe event Mage core team blessed) way to detect when Magento has fully bootstrapped so you may safely use these events?
If you attempt to use certain features in the pre-bootstrapped state, the entire request will 404. The best I've come up with (self-link for context) so far is something like this
class Packagename_Modulename_Model_Observer
{
public function observerMethod($observer)
{
$is_safe = true;
try
{
$store = Mage::app()->getSafeStore();
}
catch(Exception $e)
{
$is_safe = false;
}
if(!$is_safe)
{
return;
}
//if we're still here, we could initialize store object
//and should be well into router initialization
}
}
but that's a little unwieldy.
I don't think there is any event tailored for that.
You could add you own and file a pull request / Magento ticket to include a good one.
Until then I think the only way is to use one of the events you found and do some checks on how far Magento is initialized.
Did you try to get Mage::app()->getStores()? This might save you from the Exception catching.
I am trying to use a SOAP Client-Server in my computer and it doesn't look like it is going to work, I am getting this error Error Fetching Http Headers when I try to run my SOAP Client.
I have been looking and the solution that I have encountred is to increase the default_socket_timeout from 60 to 120 seconds and it doesn't work for me, also I have seen another solution that is putting the vhost in my apache KeepAlive Off and that didn't work.
The WSDL is working fine because I try to use it in another computer and it work.
I am running PHP Version 5.3.5-1ubuntu7.4 in Linux Mint using Zend Framework, I hope some of you can help me fix this thank you.
I'm sorry but I don't know what you are using to set up your SOAP service.....
If you can give more information about your SOAP service (poss Zend_Soap given the Zend Framework tag) etc that would be great.
Also, as a quick alternative, you say you've looked at the WSDL on another computer, perhaps try the application in an alternative environment to ensure it's not an environment issue.
May be a simple issue with your client-server code.
UPDATE: Ok so I realised the example I mentioned yesterday wasn't fully implemented so I've hacked something together quickly that you can try to see if it works in your environment.
The code is a mix of something I found here (an example of Zend_Soap_Server) and something from another SO question here (an example of a basic SOAP service test).
I've tested it at my end using ZF 1.11 and the example I'm outlining uses the default Application path you get with a new ZF project (e.g models are in directory application/models so the model shown is headed up Application_Model_Classname).
If it works, you can tweak accordingly....if it doesn't work we can try something else.
Start by creating a new SOAP controller and set the class up like this:
<?php
class SoapController extends Zend_Controller_Action
{
public function init()
{
ini_set("soap.wsdl_cache_enabled", "0"); //disable WSDL caching
$this->_helper->layout()->disableLayout(); //disable the layout
$this->_helper->viewRenderer->setNoRender(); //disable the view
}
public function indexAction ()
{
if (isset($_GET['wsdl'])) {
//return the WSDL
$this->handleWSDL();
} else {
//handle SOAP request
$this->handleSOAP();
}
}
private function handleWSDL ()
{
$strategy = new Zend_Soap_Wsdl_Strategy_AnyType();
$autodiscover = new Zend_Soap_AutoDiscover();
$autodiscover->setComplexTypeStrategy($strategy);
$autodiscover->setClass('Application_Model_SoapService');
$autodiscover->handle();
}
private function handleSOAP ()
{
$server = new Zend_Soap_Server(null,
array('uri' => "http://YOURDOMAIN/soap?wsdl"));
$server->setClass("Application_Model_SoapService");
$server->handle();
}
public function testAction()
{
$client = new Zend_Soap_Client("http://YOURDOMAIN/soap?wsdl");
try {
echo $client->testMethod('test');
} catch (Exception $e) {
echo $e;
}
}
}
In the class above, the WSDL is automatically generated using Zend_Soap_Autodiscover with a SoapService.php file at application/models/SoapService.php used as the template. Note the DocBock comments above each method in your target class are integral to this process.
Next create the SoapService.php file in the default models folder:
<?php
class Application_Model_SoapService
{
/**
* testMethod
*
* #param string $string
* #return string $testSuccess
*/
public function testMethod(string $string)
{
$testSuccess = 'Test successful, the message was: ' . $string;
return $testSuccess;
}
}
If all is working as it should be you can visit:
http://YOURDOMAIN/soap?wsdl
to see the WSDL and visit:
http://YOURDOMAIN/soap/test
to get a success message with the string you specified in the client request within the testAction() code in the SoapController class as part of the message.
Let me know if it's working or not and we can go from there.
I'll be able to have another look on Monday.