So I started using this little library for creating a RESTful PHP server, right here.
In the code, I noticed that it appears that the comments are actually significant, in other words, if I change the comments, it actually changes the behavior of the code. Is this normal practice? I've never seen this used before and it seems weird to me to not ignore comments.
class TestController
{
/**
* Returns a JSON string object to the browser when hitting the root of the domain
*
* #url GET /
*/
public function test()
{
return "Hello World";
}
/**
* Logs in a user with the given username and password POSTed. Though true
* REST doesn't believe in sessions, it is often desirable for an AJAX server.
*
* #url POST /login
*/
public function login()
{
$username = $_POST['username'];
$password = $_POST['password']; //#todo remove since it is not needed anywhere
return array("success" => "Logged in " . $username);
}
/**
* Gets the user by id or current user
*
* #url GET /users/$id
* #url GET /users/current
*/
public function getUser($id = null)
{
// if ($id) {
// $user = User::load($id); // possible user loading method
// } else {
// $user = $_SESSION['user'];
// }
return array("id" => $id, "name" => null); // serializes object into JSON
}
Basically, the #url blocks actually define what request types to which URLs call the function below them. What is the scope of this, does it have to be the #lines right above the function? Is this standard PHP practice?
It is PHP Doc. See https://phpdoc.org/docs/latest/guides/docblocks.html and specifically https://phpdoc.org/docs/latest/guides/docblocks.html#tags
A tag always starts on a new line with an at-sign (#) followed by the name of the tag. Between the start of the line and the tag’s name (including at-sign) there may be one or more spaces or tabs.
Erm... Yes and No!
No, in the sense that it's not a normal PHP feature. In PHP, a comment is a comment and PHP makes no attempt to parse its content.
Yes in the sense that because PHP won't parse the comment, developers sometimes use it as a place to store data for their libraries. The Symfony framework is a good example.
In this case, the library you installed is parsing the comments in the class RestServer.php itself. You can read the class yourself, although there's some pretty hardcore PHP and Regex in there.
Related
I'm very new to Laravel and I was given a Laravel project, where I need to add some new features. The person, who has previously worked on that project hadn't left even a single comment in the code and now I must make my own scenarios about the features.
I have a controller, defined with some functions (dashboard, show_project, save_project etc.) and in one of my function, I need to use the result of calling other function.
In the concrete example, the call is made from "http://127.0.0.1:8000/username/project_slug" - there is a button "Save" and post function, called on onClick event. The function, whose output I need is normally called on "http://127.0.0.1:8000/username/project_slug/svg", which returns a view.
For better understanding, there's an example of the flow:
The user wants to save his/her project (an UML diagram) but in order to have a thumbnail, a function which generates a view (SVG format) will be called and the idea is, to take the HTML content of the page, which is on "http://127.0.0.1:8000/username/project_slug/svg" and to pass it to another API in order an image to be generated.
So far, I tried with cURL, file_get_contents, file_get_html, render methods but when I return the output, the server just keeps waiting and shows no error messages.
//The both functions are in ProjectController.php
/**
* A function, for saving the json file, where the whole of the diagram
* components are described. From the frontend we receive the project_id and
* the project_data(the json content).
*/
public function save_project(Request $request) {
$input = $request->only(['project_id', 'project_data']);
/*
Here we need to call the other function, to render the HTML content
and to pass it to the other API. Then we save the result with the
other information.
*/
/*
What I've tried?
$new_link = 'http://' . $_SERVER['HTTP_HOST'] . "/$username"
."/$project_slug" . "/svg";
$contents = file_get_contents($new_link);
return $contents;
*/
//In the same way with cURL.
$project = Project::where('user_id',session('userid'))
->where('id',$input['project_id'])->first();
$project->project_data = json_encode($input['project_data']);
if($project->save()) {
return ["status"=>"saved"];
}
else {
return ["status"=>"error"];
}
}
/**
* A function, which takes the the Json content (project_data) from the
* database and passes it to the view, where the Json is transformed in HTML
* tags.
*/
public function generate_svg(Request $request,$username,$project_slug) {
if(session('username')!=$username) {
return redirect("/");
}
$userid = session('userid');
$project = Project::where([
'user_id' => $userid,
'slug' => $project_slug,
])->first();
if(!is_null($project)) {
return view('svg',compact('project'));
}
}
I've read about some possible ways, including Guzzle request but maybe I haven't understood correctly the idea:
If I need to make a Guzzle request from my controller to the other function inside my controller, do I need an API configuration?
What I mean? Example:
Before saving the project, the user is on this URL address "http://127.0.0.1:8000/hristo/16test". Inside the controller, I have in session variables the token, the username(hristo) and i can get the project_name(16test) from the URL but after passing this URL to the generate_svg function, there is no indication of error or success.
So I'm missing some kind of token information?
If you just need the response of the other function you can just use
$response = $this->generate_svg($request, $username, $project_slug);
If you'll need to use this function from a different controller you can use this
app('App\Http\Controllers\UsernameController')->generate_svg($request, $username, $project_slug);
Does Phalcon support content negotiation out-of-the-box or is there some easy-to-implement solution? I'm scouring the 'nets and not seeing it.
Thanks!
Short answer is no and thank god for that, or we'd have another 100 bugs for a non-major component :)
You can easily plug an existing library, like Negotiation, into DI and use it later globally throughout the app.
$di->setShared('negotiator', function(){
return new \Negotiation\Negotiator();
});
$bestHeader = $di->getShared('negotiator')->getBest('en; q=0.1, fr; q=0.4, fu; q=0.9, de; q=0.2');
Keep in mind that with the default server config (.htaccess / Nginx) from examples static files will be served as is, without interception by Phalcon. So, to server files from the server it would be best to create a separate controller / action to handle that rather than making all request go through your app.
Edit:
If it's simply about enabling your app sending either xml or json based on the common distinction (header, param, method), then you can easily accomplish it without external frameworks. There are many strategies, the simplest would be to intercept Dispatcher::dispatch(), decide in there what content to return and configure the view and response accordingly – Phalcon will do the rest.
/**
* All controllers must extend the base class and actions must set result to `$this->responseContent` property,
* that value will be later converted to the appropriate form.
*/
abstract class AbstractController extends \Phalcon\Mvc\Controller
{
/**
* Response content in a common format that can be converted to either json or xml.
*
* #var array
*/
public $responseContent;
}
/**
* New dispatcher checks if the last dispatched controller has `$responseContent` property it will convert it
* to the right format, disable the view and direcly return the result.
*/
class Dispatcher extends \Phalcon\Mvc\Dispatcher
{
/**
* #inheritdoc
*/
public function dispatch()
{
$result = parent::dispatch();
$headerAccept = $this->request->getHeader('Accept');
$headerContentType = $this->request->getHeader('Content-Type');
$lastController = $this->getLastController();
// If controller is an "alien" or the response content is not provided, just return the original result.
if (!$lastController instanceof AbstractController || !isset($lastController->responseContent)) {
return $result;
}
// Decide what content format has been requested and prepare the response.
if ($headerAccept === 'application/json' && $headerContentType === 'application/json') {
$response = json_encode($lastController->responseContent);
$contentType = 'application/json';
} else {
$response = your_xml_convertion_method_call($lastController->responseContent);
$contentType = 'application/xml';
}
// Disable the view – we are not rendering anything (unless you've already disabled it globally).
$view->disable();
// Prepare the actual response object.
$response = $lastController->response
->setContent($response)
->setContentType($contentType);
// The returned value must also be set explicitly.
$this->setReturnedValue($response);
return $result;
}
}
// In your configuration you must insert the right dispatcher into DI.
$di->setShared('dispatcher', function(){
return new \The\Above\Dispatcher();
});
Just thought that you can probably achieve the same using dispatch loop events. The solution in theory might look more elegant but I never attempted this, so you might want to try this yourself.
/**
* This function allows you to overrule the automatically generated scopes,
* so that you can ask for more or less permission in the auth flow
* Set this before you call authenticate() though!
* #param array $scopes, ie: array('https://www.googleapis.com/auth/plus.me', 'https://www.googleapis.com/auth/moderator')
*/
public function setScopes($scopes) {
$this->scopes = is_string($scopes) ? explode(" ", $scopes) : $scopes;
}
Above code is from google/Client.php,
Questions:
in comments, what does this mean: https://www.googleapis.com/auth/plus.me?
If i donot setScopes, what is the default scope?
in comments, what does this mean: https://www.googleapis.com/auth/plus.me?
That scope is specific to the user making the call and says get me all the generic google plus information (name profile picture etc.).
If i do not setScopes, what is the default scope?
No default scope, it will throw an error if you fail to specify.
I made some classes having a lot of methods documented properly using PHP (something like a library).
Now, what the other developers will do is just require the PHP library I made in their code and use the predefined functions in it.
Is it possible to hide the PHP code (of the library I made) from the other PHP developers (requiring the file) and just show them the function name, parameters and its documentation without showing the code inside it? I'm not talking about obfuscation, which can be reversible, I'm talking about preventing users to actually see any code.
eg.
/**
*
* CREATE A NEW THREAD
* #param unknown_type $uid User ID of person who is creating the thread
* #param unknown_type $participant An array having collection of UID of people who are participating in this conversation
* #param unknown_type $msgtype Message Type Flags (1-normal, 2-chat, 3-sent as email, 4-profile post, 5-group post, 6-customer support)
* #param unknown_type $subject Subject of the thread
* #param unknown_type $tname Thread Name
* #param unknown_type $tpic Thread Cover Picture (Defaults to "")
* #param unknown_type $tflag Thread Flag (1-allowed,2-under review,3-blocked) (Defaults to 1)
* #return string|Ambigous <string, unknown> Thread ID on success, "" on failure
*/
public function createthread($uid,$participant,$msgtype,$subject,$tname,$tpic="",$tflag="1")
{
$randobj=new uifriend();
$tid=$randobj->randomstring(30,DB_MESSAGE,MSG_OUTLINE,msgoutline_tid);
$socialobj=new socialoperations();
$listid=$socialobj->createlist("threadlist_".$tid, "2",$msgtype,"1",$uid);
if($socialobj->addtolist($participant, $listid, $uid)!="SUCCESS")
{
return "";
}
if($listid=="")
{
$lasterror="An error occured in creating thread! Unable to Create Lists!";return "";
}
$dbobj=new dboperations();
$res=$dbobj->dbinsert("INSERT INTO ".MSG_OUTLINE." (".msgoutline_tid.",".msgoutline_subject.",".msgoutline_fid.",".msgoutline_participantid.",".msgoutline_msgtype.",".msgoutline_threadpic.",".msgoutline_threadflag.") VALUES
('$tid','$subject','$uid',$listid,'$msgtype','$tpic','$tflag')",DB_MESSAGE);
if($res=="SUCCESS")
{
return $tid;
}
else
{
$lasterror="Unable to create Thread!";return "";
}
}
The other developers must only be able to see the documentation I wrote above the function with the function name and parameters, but the code must not be accessible to them in any way.
Why I want this: I have a lot of secure code in my PHP file which I don't want to show to the other developers, but still allow them to call the functions and read the returned values.
You can't hide your code from other developers if you want to allow them call your functions directly. What you can do is to make a Web Service and give it's documentation to other developers.
Because I had a meta post so this was reopened and another meta post for formatting this question, I'll do my best to properly answer this question. Note that this is only a way of doing this, with its limitations stated at the end of the post.
The API
The remote server
You could create a web API in a different domain and access it from your main domain. I think the best way for explaining how it works is with a practical example. Imagine that your library includes the function 'joinstrings()', which takes 2 arguments. Then you have it in your separated web:
http://apiweb.com/functions.php
<?php
// Your API. I hope the real one is more complex than this (;
function joinstrings($s1, $s2)
{
return $s1 . $s2;
}
// More functions
The remote server access point
This is the public (but key-required) accessible page.
http://apiweb.com/joinstrings/index.php
<?php
// Check if the key is valid and if $v1 and $v2 aren't empty. Else, 'exit;'
include '../validate.php';
// Your API
include '../functions.php';
// The called function
echo joinstrings(urldecode($_GET['v1']), urldecode($_GET['v2']));
The wrapper
Now you can require all your programmers to learn how to use this API. Or, if you prefer to do it right, you'd make a wrapper that makes their life easier. You'd have a class with all the methods that you want to be accessible. You could do this wrapper with functions, but I think it's easier and better with an object and methods:
htpp://web.com/library.php
<?php
class DevelopersLibrary
{
private $Url = "http://apiweb.com/";
// Press your hand against the keyboard. A-Z0-9. Copy it in http://apiweb.com/validate.php
private $Key = "g139h0854g76dqfdbgng";
// Accesible method
public joinstrings($v1, $v2)
{
// Encode only the user input. You don't want to encode '?' nor '&'
if ($Return = file_get_contents($this->Url . 'joinstring'
'?key=' . $this->Key .
'&v1=' . urlencode($v1) .
'&v2=' . urlencode($v2)))
{
return $Return;
}
}
}
Developer's code
Finally, what your developers would do:
http://web.com/index.php
<?php
include './library.php';
$Lib = new DevelopersLibrary();
echo $Lib->joinstrings("Are you sure this is better", "than giving your developers access to the code?");
None of the code is tested, so you should expect some some typos.
Limitations
I can think of solutions for most limitations, but not to extend (more) this post I won't write them here. Ask for a solution to a limitation if you need it in the comments and I'll do my best. In normal case use, none of these limitations are THAT important.
Parameters passed. Using this method as described above, you can only pass numbers or strings as function parameters. Check out json_encoding() for passing other types.
Wrong returned values when there are bugs in the API or parameters passed. If there's a bug in the API, the developers cannot fix it and the returned value might be wrong. Now that might seem trivial, but what if they are trying to retrieve the join of 2 strings and retrieve another [wrong] string with the error text in it? Note: consider returning valid XML and then parsing it in your wrapper.
There's only a unique key which is there for preventing random users from using your API, not to be hidden from developers.
Slower speed. I don't think this even needs explanation.
Developer's extra work. This is solved this with the implementation of the wrapper.
Url length. There's a url length limitation for most browsers of 2000 characters, although I didn't find anything in the PHP manual for file_get_contents(). Read this SO question for more info about GET.
Sure there are more but these are the main ones I could think of.
I hope this long long answer is useful for you or someone.
.. Couldn't think of a descriptive enough title. What I'm asking for is how do I do this?
I want the following 2 API calls
GET /api/users/2/duels - returns all of the duels for user 2
GET /api/users/2 - returns the profile for user 2
Since PHP doesn't support method overloading, it isn't clear to me how to make this work.
Currently I have the function
function get($id, $action){
//returns data based on action and id
}
And I can't just make
function get($id){
//returns profile based on id
}
because of said reasons.
Any help is greatly appreciated!!!
You can use the #url phpdoc decorator to tell restler of any special invocation schemes that doesn't match the direct class->method mapping.
/**
* #url GET /api/users/:userId/duels
*/
public function getDuels($userId)
{
}
.. should probably work.
One approach is handling both cases in same function with a conditional block as shown below
function get($id, $action=null){
if(is_null($action)){
//handle it as just $id case
}else{
//handle it as $id and $action case
}
}
if you are running restler 3 and above you have to disable smart routing
/**
* #smart-auto-routing false
*/
function get($id, $action=null){
if(is_null($action)){
//handle it as just $id case
}else{
//handle it as $id and $action case
}
}
Another approach is to have multiple functions since index also maps to root, you have few options, you can name your functions as get, index, getIndex
function get($id, $action){
//returns data based on action and id
}
function index($id){
//returns profile based on id
}
If you are using Restler 2 or turning smart routing off, order of the functions is important to fight ambiguity
If you are running out of options for function names you can use #url mapping as #fiskfisk suggested but the route should only include from method level as the class route is always prepended unless you turn it off using $r->addAPIClass('MyClass','');
function get($id){
//returns data based on action and id
}
/**
* #url GET :id/duels
*/
function duels($id)
{
}
HTH