Can anyone tell me what I'm doing wrong? I just learned how to create a function and display it inside a dynamic table - I think. I've been tweaking it, changing the variables, etc., but I can't get it to work.
I inserted the following script before the while loop.
function get_stars2($Latin)
{
$stars2 = '';
{
switch($Latin) {
case 'Aix sponsa':
case 'Gavia immer':
return '<sup><span style="color: #f00;"><b>+</b></span></sup>';
break;
default:
return $stars2;
break;
}
}
}
$Latin is actually defined inside the while loop, but I was told that doesn't matter; the function can take care of that outside the while loop.
And here's how I inserted the function inside the table:
<td>'.$row['Symbol'].'</a>'.get_stars2($Latin).$DesigAst.'</td>
Note: Someone commented that it's bad form to use functions to display HTML, so let me explain what's going on here. I was working on a rather complex dynamic reference table - actually a series of tables. One of my files was suffering from code bloat, so I put a series of PHP switches in a separate file and included them, using require_once. That was how I learned that require_once nixes the while loop; in other words, an action can only occur once. If I change require_once to require, then that entire file is included once for every row in my table.
So I asked about alternatives, and I was told I need to use a function, something I have very limited experience with. There were some other alternatives, but they were a little over my head. So I think I either need to learn how to work with functions or jazz up my database tables so they can handle all the extra annotation and footnotes in my reference tables.
Why all of the convoluted logic?
function get_stars2($Latin) {
if ($Latin) return '<sup><span style="color: #f00;"><b>+</b></span> </sup>';
}
Even better, you could just wrap it in
if ($Latin) { foo(); }
Also, it's considered bad form to have your PHP functions return HTML.
Also, your code works. Maybe you're not using the echo command.
Related
I figure I'd better ask than make a fool of myself some time when I get my code reviewed.
I am making a website, trying to follow MVC design architecture, although it might not even be MVC but I figured it works nice.
Currently, I am doing something like this;
I have a render() function that takes a string $template and array of arguments,
render($template, $vars=[]){
extract($vars);
ob_start();
include($template.'.php');
ob_flush();
}
and outputs the view. In a controller, I then do this,
Class WhicheverController
public static function defaultAction(){
$article=Article::getOneByID(array('id'=>$_GET['id'], 'user'=>User::currentUser())); //returns database result as object
render('_header', array('title'=>$article->title));
render('header', array('user'=>User::currentUser()));
render('sidebar', array('user'=>User::currentUser()));
$content=Comment::getByArticleID(array('article'=>$article->id)); //returns object
//of root comments, highest level in comment-replies tree
$comments=function() use ($content){
foreach($content as $comment){
$threads[]=function() use ($comment){
render('comment', array('comment'=>$comment));
}
}
render('pagination', array('content'=>$threads)); //pagination paginates based on number of
//items in content, and echoes it or runs it if it is_callable
}
render('article', array('article'=>$article, 'comments'=>$comments));
}
And finally in article.php;
//some html with echoing $article->variablethis or variablethat
<div class="comments">
<?= $comments(); ?>
</div>
Now, the way I figured it out, ob_flush() sends to client prematurely, so I get headers with styles and header and sidebar printed as I am getting the neccessary variables. That is good (if that's how it works).
Then, I want to do the same with the main content - article and comments. First I get the comments and article data, and pass it to article.php. But I have to do some pagination logic first before sending it away, and that would delay rendering of article.php. So I create an anonymous function which does that, and pass that as a variable function $comments(); which does it's logic when it is called.
This is only one example. In one controller I first get top rated articles of today, yesterday and current week, and create anonymous function for each one, before passing it to frontpage.php. Frontpage.php does this, essentialy;
//some html markup
<?= $articles24h(); ?>
//more html
<?= $articles48h(); ?>
//and so on
Now, when I thought of this I thought it was brilliant but as I started to add more variable functions I noticed my code was starting to become longer and I presume anyone other than me illegible.
My question is: is this bad practice; this usage of anonymous functions, variable functions and ob_flush() (more importanntly, does it do what I think it does)?
EDIT: The code itself works. In here I might've made some logical mistakes, but the point is to display the usage of functions.
EDIT 2: Okay, so I realized I can have one anonymous function display $articles24h and $articles48h any paginating content just by passing each as $content, like so function($content) use ($something_else, $but_common){ //yada yada }, which helps a lot, but I still don't know if this is desirable. i.e. there is a better way.
EDIT 3: I created 3 View classes, one View class with static render() method that just takes a template name and array with variables and prints out a template, and two others, PaginationView and PartialView; both have their own render() method and don't extend View class (even thought if I was a better programmer yadda yadda I' do it differently). Pagination paginates partial views that PartialView stores in a class variable and creats when constructed. If I don't need to paginate data, I just call PartialView->render()
Neat (IMO), but it looks kinda ugly in code, like so:
$content=Article::getByAuthorID(array('author'=>$profile->id), array('order'=>$order,'column'=>'time_submitted'));
$user_articles=new PartialView('_article', $content, array('length'=>60));
$user_articles=new PaginationView($user_articles->views, 10, 1);
$user_articles is then sent to
echo View::render('user_page', array('articles'=>$user_articles, 'profile'=>$profile, 'comments'=>$user_comments));
As you can see, I also do this for comments so the code gets lengthy. I don't want to change anything it I am not breaking anything, although I feel like this is a violation of DRY in some way (?)
Is there a better way to do this, without sacrificing clarity but making it more compact?
I am trying to develope good code organization habits and work exclusively with OOP in php but I can't seem to wrap my head around something.
Here is a simplified description of what I am working with:
I have all my class files in a folder '/resources/Classes'
I have all my html and javascript in '/public_html' & '/public_html/script respectively'
My question is concerning files that are the actions of forms or AJAX requests. For example 'formAction.php' and 'ajaxURL.php'. These files are not Classes and also do not contain any html or other such GUI.
I have been putting them in a folder 'resources/actions' but my gut tells me something about this is not fully OOP.
Is my usage of these files incorrect if I am trying for complete OOP? if so how can I approach this differently.
Here is an actual file from my project as a concrete example:
//file: getBalance.php
<?php
/**
* This script gets the balance of an account from the server
*/
if (!isset($Database)) {
$Database = require_once "../clear_finance_pkg.php";
}
/** #var User $User */
$User = $Database->getUserByID("1");//TODO: user should be set dynamically
$User->setAccounts($Database->getAccountsByUser($User));
if (isset($arg1)) {
$accountID = $arg1;
foreach ($User->getAccounts() as $Account) {
if ($Account->getId() == $accountID) {
$RbcChequing = RbcAccount::accountToRbcAccount($Account, "Chequing");
echo '$' . Money::toDollars($RbcChequing->getBalance());
break;
}
}
} else throw new Exception('Account ID is not set. Could not get balance');
It's difficult to say if your code is complete OOP, but i think it isn't. It looks like you are on the right track, because you are using classes, objects and methods. That's the basic of OOP. No longer large if/else statements and a lot of variables that doesn't make sense, but objects and methods without code-duplication.
I think your question in more related to the seperation of logic and view. First of all. In general it's ok to make a file for a function, but in a large application you will loose the overview. What you are doing know is combine view-related and logic-related things in one file, but actually that's not what you want. The ideal situation is full seperation of logic and view so you can create multiple seperate views based on the same logic.
My advice is to take a look at the MVC-pattern. Take a look at this link. It will help you to get a basic understanding of the MVC-pattern. Notice that you won't longer need to have a file for each function. And you have a seperation of your logic and view elements because you can use the same model in multiple views (although this is maybe not the best example).
Basically, I am implementing own cache system. Ideally, it'll look like this:
$CACHE->start($name);
//CODE
$CACHE->end();
But that is a holy grail that I do not hope to find. Basically, the $CACHE->start() checks if cache is a hit or a miss, and whether it is a hit, it skips the //CODE until $CACHE->end().
The best I have come so far, is:
if ($CACHE->start($name)) {
//CODE
}
$CACHE->end();
Since PHP supports anonymous functions, I was thinking of:
$CACHE->make($name, function() {
//CODE
});
But this code has a problem that code is not in the same variable scope. Any chance to bypass that?
Update: I have since switched to ruby, which allows to pass the block to a function, being perfect for this task.
How about a default approach? The example below is quite common and is used it memcached f.e.
function doSomething()
{
$oCache = SomeRegistry::get('Cache');
// Check for cached results.
if ($oCache->exists('someKey')) {
return $oCache->get('someKey');
}
$sCached = getSomeThing();
$this->set('someKey', $sCached);
return $sCached;
}
It is basic key value storage, and doesn't require any closure tricks.
Zend Framework includes a cache that skips $cache->end() by assuming the remainder of the page is part of the cached content.
// Default cache ID is calculated from $_SERVER['REQUEST_URI']
$zendPageCache->start();
// ....
// No need for end
It doesn't fit all use-cases though.
(A modified version of my comment)
In the anonymous function you can use the 'use' keyword to bring variables into that scope.
<?php
function () use ($container, $anythingElseYouMayWantToUse) {
//...
}
You might implement the first one with goto, but it's a very rude approach, and you will be looked at as an enemy of programming.
I'd go for the second one if I had to choose.
I'm using this code in a views field template (in this case views-view-field--all-members--uid.tpl.php):
<?php
$users_friends = flag_friend_get_friends($user->uid);
$users_friends_ids = array();
foreach ($users_friends as $id => $value) {
$users_friends_ids[] = $id;
}
?>
It basically gets the user ids of friends and puts them in an array so I can check if the field matches any of the user ids.
So my problem is that I don't want to have this within this template (for a few reasons), but if I don't I can't access the array. How can I make this array globally accessible?
Without knowing your "few reasons", I can't say if this is the answer for sure. My own reasons would probably be that I don't want the same code executing a bunch of times, and I'd rather not have the same exact code in multiple places.
I would then create a function with a static variable to hold the friends array.
function mymodule_get_friends_ids() {
// pull in the current global user variable
global $user;
// call up the static variable
static $users_friends_ids;
// return if this static var has already been set
if (is_array($users_friends_ids)) {
return $users_friends_ids;
}
// if we hit here, then this function has not been
// run yet for this page load.
// init array
$users_friends_ids = array();
// if user is anon, no need to go on
if (user_is_anonymous()) {
return $users_friends_ids;
}
// get friends array
$users_friends = flag_friend_get_friends($user->uid);
// build ids array
foreach ($users_friends as $id => $value) {
$users_friends_ids[] = $id;
}
return $users_friends_ids;
}
Now in your templates, you can call mymodule_get_friends_ids() in as many places as you want, and the working code below the first return will only get executed the first time it is called.
Coder1's advice is very good - it keeps you from populating your global variable namespace with a lot of junk. It's probably the most "elegant." It might not be the easiest to use if you are rather new to PHP (which I'm guessing might be the case if it's hard to get your head around returning arrays, but that's ok).
However, if this is really a priority, you probably don't care about having one extra global variable.
I suppose I may be stating the obvious here - but you can, at pretty much any point in execution (provided the information you need has already been generated - e.g., the $user variable has been populated), do this:
$GLOBALS['users_friends_ids'] = /* your code goes here */
Then in your template, you access this by ...
$friendsArray = $GLOBALS['users_friends_ids'];
Or you can simply use the construct
global $user_friends_ids;
when you want to initialize the variable, or access it inside a function or class (which is the case for your template files - they are called inside functions, so you need to globalize or use the $GLOBALS array, which is "automagically" all of the variables active in the global namespace).
The most "logical" place to do this would be inside a module using one of the many hooks available, to execute this code only once. hook_init() might do it for you, if the user object is already loaded at this point (not sure, you'll have to test). But you might not want to figure out making Drupal modules (it's not that difficult).
If you are doing this inside a template (and though it's not good practice, many Drupal site owners with a beginning knowledge of PHP put everything in templates), you'll want to know which template code is being executed when. Node template code tends to be executed before page template code - which is logical, since otherwise the variables for node content in the page template wouldn't be populated.
If you have listings of nodes, they'll be calling this code multiple times, so you'll end up doing something similar to what Coder1 is describing. If you don't want to create your own small module, you could put the function declaration he's written in your theme's template.php file, since it's called only once. You don't want to put function declarations in the tpl.php files, since they are sometimes called more than once (and you aren't allowed to declare functions more than once).
If you have a hard time understanding the function and the return, you can always do something like this in your code (which is very, very inelegant - but it's better to have inelegant code that you do understand, than elegant code that's you don't).
if(!isset($GLOBALS['users_friends_ids'])) {
$GLOBALS['users_friends_ids'] = /* your code here */
}
I needed to create dynamic breadCrumbs that must be realized automatically by the application. So I have the following structure in the URL for navagation:
nav=user.listPMs.readPM&args=5
then i could have a function-file whose sole purpose would be to define the user.listPMs.readPM function itself:
file: nav/user.listPMs.readPM.php
function readPM($msgId)
{
/*code here*/
}
Of course this ends up cluttering the global scope since i'm not wrapping the function withing a class or using namespaces. The best solution here seems to be namespacing it, no doubt right? But I also thought of another one:
file: nav/user.listPMs.readPM.php
return function($msgId)
{
/*code here*/
};
Yep, that simple, the file is simply returning an anonymous function. I think this is amazing because i don't need to care about naming it - since i've already properly named the file that contains it, creating a user function and yet having to name it would seem just redundant. Then in the index I would have this little dirty trick:
file: index.php
if($closure = #(include 'nav/'.$_GET['nav']))
{
if($closure instanceof Closure)
{
$obj = new ReflectionFunction($closure);
$args = explode(',',#$_GET['args']);
if($obj->getNumberOfParameters($obj)<=count($args))
call_user_func_array($closure,$args);
else
die('Arguments not matching or something...');
} else {
die('Bad call or something...');
}
} else {
die('Bad request etc.');
}
Don't even need to mention that the breadCrumbs can be nicely built latter just by parsing the value within the $_GET['nav'] variable.
So, what do you think, is there a better solution to this problem? Have you found another way to explore Closures and/or Reflection?
I like the basic idea. But the implementation is pretty much terrible. Imagine that I set nav=../../../../../../etc/passwd. That would (depending on your server configuration) allow me to access your password file, which certainly is no good.