Setting Slim parameters in order to debug - php

I enjoy using Slim, but am frustrated on how to debug it. Lets say I have the following routing. I can do a cURL request, and see the output, but instead I wish to go through the script line by line with my IDE debugger which happens to be NuShere's phpED. While I haven't quick figured it out, I am pretty sure I can make phpED do a POST request, but definitely not a PUT or DELETE request, so there is no point.
Is there any way to do so? I can force $_SERVER['REQUEST_URI'] and $_SERVER['REQUEST_METHOD'] to any value easy enough, and can also change the $_GET superglobal, but body data (i.e. $_POST) isn't so simple.
<?php
$app = new \Slim\Slim();
$app->get('/books/:id', function ($id) {
//Show book identified by $id
});
$app->post('/books', function () {
//Create book
});
$app->put('/books/:id', function ($id) {
//Update book identified by $id
});
$app->delete('/books/:id', function ($id) {
//Delete book identified by $id
});
$app->get('/toys/:id', function ($id) {
//Show toy identified by $id
});
$app->post('/toys', function () {
//Create toy
});
$app->put('/toys/:id', function ($id) {
//Update toy identified by $id
});
$app->delete('/toys/:id', function ($id) {
//Delete toy identified by $id
});

Based on Blake's comments, I created the following method, and call it in a constructor. After each request, a new test file is created which can be debug.
private function createTest($params,$method)
{
$inputs=print_r($params,1);
$method=strtolower($method);
$method2=($method=='put' || $method=='delete')?'post':$method;
$html=$this->createTestInputs($params,null,null); //array, previous inputs, name prefix
$html=<<<eod
<p>URL: $_SERVER[REQUEST_URI]</p>
<p>METHOD: $method</p>
<p>INPUTS:<pre>$inputs</pre></p>
<form action="$_SERVER[REQUEST_URI]" method="$method2">
$html
<input type="hidden" name="_METHOD" value="$method"/>
<input type="submit" value="Test"/>
</form>
eod;
file_put_contents($_SERVER['DOCUMENT_ROOT'].'/test.php', $html);
}
private function createTestInputs($params,$html,$prefix)
{
foreach($params as $name=>$value) {
if(is_array($value)) {
$html=$this->createTestInputs($value,$html,$name);
}
else {
$html.="<input name='".($prefix?$prefix.'['.$name.']':$name)."' type='hidden' value='$value' />\n";
}
}
return $html;
}

If You need debug info in case of exception:
$app->config('debug', true);
also You can use Firebug + FirePHP as explained here: https://www.sitepoint.com/debug-php-firebug-firephp/
Tracy
but I like the interface of debugging so I found something like this: https://github.com/nette/tracy
checkout this gist: https://gist.github.com/1f7/b2d2846777e71f48e43a2ef7acc0a655 (it has little bugs in example, but You can handle it)
demo is here: https://nette.github.io/tracy/tracy-debug-bar.html
screenshot of my test implementation:
but I really recommend You to use normal IDE like PHPStorm and debug Your app like a boss (:
watch: https://www.youtube.com/watch?v=rqDDJfG6ip4 or this: https://www.youtube.com/watch?v=_U8xrj0-PXU
p.s. for question of properly configuring Tracy, write in comment (;

Slim3 Tracy gist increased to Slim3 Debug Bar package.
More screenshots here.
And i agree - PHPStorm is better for debug, but quick discover vars easier in Tracy.
In the near future plan add Echo Console (JQuery Terminal)

Related

Slim Framework how to capture route parameter by Name in interceptor

I'm using v2 of Slim framework. Most of the APIs are of the format below:
https://abcXX.com/:version/customer/:CustomerID/XXX/....
So almost all of the APIs have :version and :CustomerID which is provided in the URL.
I'm writing a hookup like so, and wanted to know if there is any way to globally intercept it in hookup function
public function call()
{
$this->app->hook('slim.before.dispatch', array($this, 'onBeforeDispatch'));
$this->next->call();
}
public function onBeforeDispatch()
{
$this->app->is_development_environment = IS_DEVELOMPENT_ENVIRONMENT;
$route_params = $this->app->router()->getCurrentRoute()->getParams();
$param_version = ???
}
I don't know how to get the :version's value from URL parameters. If I could do that, I'd be able to do some global handling at the very top, instead of having to write the code in each API handler function.
Please suggest how to do this.
I recommend avoiding hooks for determining which version of your API is being called. Consider grouping routes instead.
And I suggest using middlewares instead of hooks unless you really need hooks, they are different in nature.
Anyway, if you want to use hooks, here is an example of how to do it:
<?php
require_once ('../vendor/autoload.php');
$app = new \Slim\Slim();
$app->hook('slim.before.dispatch', function() use($app){
$params = $app->router()->getCurrentRoute()->getParams();
// Use $params['version'] to do what you want
print "<pre>";
print_r($params);
print "</pre>";
});
$app->get('/:version/hello/:name', function ($version, $name) {
print "Hello, " . $name;
});
$app->get('/:version/customer/:CustomerID/XXX', function($version, $customerId){
print "Customers route";
});
$app->run();
Now if you open <host address>/2.1/hello/foo or <host address>/2.1/customer/1000/XXX you see in both cases you see ['version'] => 2.1 in $params.
But if you go with this approach, I think you should define all your route callbacks to accept a $version as their first parameter, which is absolutely unnecessary and redundant and makes code manipulation harder. But if you group routes, then you'll have something like:
$app->group('/2.1', function () use ($app {
$app->get('/hello/:name', function ($name) {});
$app->get('/customer/:CustomerID/XXX', function($customerId){});
});
which is cleaner and does not need extra effort.
Use Callback Function or Make a function to get params like it :-
$app->get('/books/:one/:two', function ($one, $two) {
echo "The first parameter is " . $one;
echo "The second parameter is " . $two;
});

php - phantomjs alerts

I am not very familiar with javascript and not sure how to handle alerts in php script when using phantomjs.
This is my code:
$this->clickcontrol(Constants::LINK, 'delete', false);
$this->acceptAlert();
So how should I change this to handle alerts in phantomjs
This looks like PHPUnit's PHPUnit_Extensions_Selenium2TestCase.
When faced with this, I have created following function (I put it into a common base test case class myself, but it also can be in your test class):
protected function waitForAlert($expectedText, $timeout = 10000)
{
$this->waitUntil(
function () use ($expectedText) {
if ($this->alertText() == $expectedText) {
return true;
}
},
$timeout
);
$this->acceptAlert();
}
Then in the test itself you can use it as such:
$this->waitForAlert('You need a complete profile');
If there is no alert it will fail after the timeout set
Hope this helps ;)

MVC pattern with AJAX calls

This is a weird question about a php mvc pattern with ajax calls. The purpose is make a better and dynamically web apps. Let me explain you:
When I learn php, I used this pattern specifically :
model.php
<?php
class myClass {
private $attrOne;
private $attrTwo;
public function getAttrOne() {
return $this->attrOne;
}
public function setAttrOne($attrOne) {
$this->attrOne = $attrOne;
}
public function getAttrTwo() {
return $this->attrTwo;
}
public function setAttrTwo($attrTwo) {
$this->attrTwo = $attrTwo;
}
// ----------------------------------------------------
public function doSelect() {
//some code
}
public function doInsert() {
//some code
}
public function doUpdate() {
//some code
}
public function doDelete() {
//some code
}
}
controller.php
<?php
require "../models/model.php";
if(isset($_POST['action'])) {
$action = $_POST['action'];
if(is_callable($action)) {
$action();
}
}
function registerSomething(){
$model = new myClass();
$model->setAttrOne($_POST['attrOne']);
$model->setAttrTwo($_POST['attrTwo']);
$return = $model->doInsert();
echo $return;
}
function registerSomething2(){
// more app logic code and other stuff
}
view.php -> this is most a pure html file with php extension
<div id="result"></div>
<form id="register" role="form" >
<input type="text" id="attrOne" name="attrOne"/>
<input type="text" id="attrTwo" name="attrTwo"/>
</form>
<script src="script.js" type="text/javascript"></script>
And the script.JS
$('#register').submit(function() {
var action = 'registerSomething';
$.ajax({
data: $(this).serialize() + '&action='+action,
url: '../controlllers/controller.php',
type: 'POST',
success: function(response) {
$('#result').html(response);
}
})
return false;
})
So, what do you think about this pattern? is this pattern efficient?
What is the best way to do ajax calls with a proper mvc pattern in php?
Is this a best practice?
If your goal was to specifically implement something MVC-like, then you have utterly failed. This setup has absolutely nothing to do with MVC. To be honest, it seems that you are way too inexperienced for tackling something like that.
If instead this is your first attempt in applying Separation of Concerns on your code, then it's appropriate. Though, I still wouldn't put that type of code in the production.
My recommendation for you would be: stop trying to "do MVC" for now, and focus instead on improving your general understanding of web development.
You should google the following topics for PHP:
url routing
autoloading and PSR4
difference between active record and data mapper pattern
request abstraction

CakePHP: How to use a view element inside of a controller

I'm trying to figure out how to use one of my view elements inside of a controller...
I know, I know: "Don't do that!" (99% of the time this is the correct answer)
But I think I actually have a good reason. The action is handling an AJAX request which returns markup. The returned markup is a list which I display everywhere else using an element. So in an effort to keep my code DRY, I think it's appropriate to do this here.
Is this possible?
Easy:
$view = new View($this, false);
$content = $view->element('my-element', $params);
Also:
DON'T DO THAT ANYMORE!!!
Sometimes, you need to render a CakePhp element from a view and inject its content into the page using AJAX the same time. In this case rendering element as a regular view from controller is better than creating a dedicated view that just contains <?php echo $this->element('some_element') ?>, and may be done this way:
<?php
public function ajax_action() {
// set data used in the element
$this->set('data', array('a'=>123, 'b'=>456, 'd'=>678));
// disable layout template
$this->layout = 'ajax';
// render!
$this->render('/Elements/some_element');
}
I know this is an old question and other people have already given basically the same answer, but I want to point out that this approach (provided by Serge S.) ...
<?php
public function ajax_action() {
// set data used in the element
$this->set('data', array('a'=>123, 'b'=>456, 'd'=>678));
// disable layout template
$this->layout = 'ajax';
// render!
$this->render('/Elements/some_element');
}
...is not a hacky workaround, but is in fact the recommended approach from the CakePHP docs for this common and legitimate use case:
If $view starts with ‘/’, it is assumed to be a view or element file
relative to the /app/View folder. This allows direct rendering of
elements, very useful in AJAX calls.
(Again: Credit to Serge S. for the code above)
$this->view = '/Elements/myelement';
You should use a client-side template. You should never return mark-up from a web service or API, just data. Have your JavaScript take the data, and then format it how you wish.
For example:
function getItems() {
$.get('/some/url', function(response) {
if (response.data.length > 0) {
for (var i = 0; i < response.data.length; i++) {
var item = response.data[i];
$('.results').append('<li>' + item.title + '</li>');
}
}
});
};
This is just an example written off the cuff. Obviously you’ll need to write your own implementation.
The way I did any ajax handling in Cake was to have my own AjaxController. Any interaction of ajax-kind goes there, which in-turn uses their own views (and view partials / elements). That way you can keep your code DRY and isolate and propagate all ajax use-cases there.
Example excerpt:
<?php
class AjaxController extends AppController {
/**
* (non-PHPdoc)
* Everything going to this controller should be accessed by Ajax. End of story.
* #see Controller::beforeFilter()
*/
public function beforeFilter() {
parent::beforeFilter();
$this->autoRender = false;
$this->layout = false;
if (!$this->request->is('ajax')) {
$this->redirect('/');
}
}
public function preview() {
if ($this->request->is('ajax')) {
$this->set('data', $this->data);
$this->render('/Elements/ajaxpreview');
}
}
?>
Here's the source: https://github.com/Sobient/dosspirit/blob/master/app/Controller/AjaxController.php

Instantiating a AMF PHP class not working

I am trying to use AMF PHP to pass variables to a flash file, thus far I cannot see anything wrong with my code, but I have very little experience with creating classes, so here it goes, here is my code,
index.php:
<?php
include "amfphp/services/flashMe.php";
$session = true;
if ($session == true) {
$uid = '12345';
$thing = new flashMe;
$thing->push($uid);
} else {
//login
}
?>
flashMe.php:
<?php
class flashMe {
public function __construct() {
}
public function push($one)
{
return $one;//sends the uid to the flash file?
}
}
?>
Flash is looking for the flashMe class and the push method within that class, but I keep getting null variables in my flash file when I run it, is there something wrong with this code?
Thanx in advance!
Your index.php file is unnecessary.
Your second file is incomplete. Here is the example from the docs for their "hello world" class file:
<?php
class HelloWorld
{
function HelloWorld()
{
$this->methodTable = array
(
"say" => array
(
"access" => "remote",
"description" => "Pings back a message"
)
);
}
function say($sMessage)
{
return 'You said: ' . $sMessage;
}
}
?>
This file should be saved as "HelloWorld" matching the "class HelloWorld" you have named in the php file (you did this part right with FlashMe).
The example file in the docs for the Flash piece (in actionscript) is here:
import mx.remoting.*;
import mx.rpc.*;
import mx.remoting.debug.NetDebug;
var gatewayUrl:String = "http://localhost/flashservices/gateway.php"
NetDebug.initialize();
var _service:Service = new Service(gatewayUrl, null, 'HelloWorld', null , null);
var pc:PendingCall = _service.say("Hello world!");
pc.responder = new RelayResponder(this, "handleResult", "handleError");
function handleResult(re:ResultEvent)
{
trace('The result is: ' + re.result);
}
function handleError(fe:FaultEvent)
{
trace('There has been an error');
}
The gateway URL should go to wherever your services can be reached. I'm sure if you try a few you'll find the right one. The neat thing about amfphp is that it allows you to also test your services out before you try implementing them in the gateway (if you go to the URL in your browser).
I'm pretty new to AMFPHP as well, but I've found the docs to be extraordinarily useful. If you need more help on classes, you can find more info on the PHP docs page.
You missed the parenthesis after new flashMe
$thing = new flashMe();
$thing->push($uid);
Amfphp or Zend AMF only allow you to call public methods on a remote class that is exposed by your gateway. You example is not a class and therefore no remote method can be called. This looks more like something that you would do with an http post.
http://framework.zend.com/manual/en/zend.amf.server.html

Categories