treehouse : what would you like to learn today?
Web Design Web Development iOS Development

[Solved] Output value of nested arrays?

  • Is there a simple way to output a value that's nested inside multiple arrays?

    Array ouput:

      Array
      (
          [auth] => Array
              (
                  [uid] => 50799167
                  [info] => Array
                      (
                          [name] => Chris Burton
                          [nickname] => chrisburton
                          [urls] => Array
                              (
                                  [twitter] => http://twitter.com/chrisburton
                                  [website] => http://chrisburton.me
                              )
    
                          [location] => New York
                          [description] => Letterer & Typographer
      http://dribbble.com/chrisburton
                          [image] => http://a0.twimg.com/profile_images/3106804659/f100a994784f6e8059bb15125f286667_normal.png
                      )
    
                  [credentials] => Array
                      (
                          [token] => Token Removed
                          [secret] => Secret Removed
                      )
    
                  [raw] => Array
                      (
                          [id] => 50799167
                          [profile_sidebar_fill_color] => DDEEF6
                          [created_at] => Thu Jun 25 22:48:35 +0000 2009
                          [contributors_enabled] => 0
                      )
    
  • Do you know which value you need?

  • @traq Well, let's say I need multiple values.

    [id] [screen_name] [image]

  • I was able to get it by doing this

      echo $auth['raw']['id'];
    

    Just thought they're might be a better way. Maybe something like:

      echo $raw->screen_name;
    
  • If this is something you'll be doing regularly (i.e., more than once, ever), I'd suggest writing a function to extract them into a more useful format. Something like

      <?php
      // $array is your array from twitter
      function twitter_user_info( array $array ){
    
          // check that each item exists in $array
          $id = isset( $array['auth']['uid'] )? 
              $array['auth']['uid']: 
              null;
          $nick = isset( $array['auth']['info']['nickname'] )?
              $array['auth']['info']['nickname']:
              null;
          $img = isset( $array['auth']['info']['image'] )?
              $array['auth']['info']['image']:
              null;
    
          // if any are missing, something went wrong
          if( $id === null || $nick === null || $img === null ){
              return false;
          }
    
          // array is both numeric and assoc
          // so you can access it by key
          // or by index # - e.g., using 
          //    `list( $id,$name,$img ) = twitter_user_info()`
          $user_info = array(
              $id
             ,$nick
             ,$image
             ,'id'            => $id
             ,'screen_name'   => $nick
             ,'image'         => $image
          );
    
          return $user_info;
      }
    

    If you want an object (as in your last comment), use the same function as above but replace $user_info with this:

      $user_info = (object)array(
          'id'            => $id
         ,'screen_name'   => $nick
         ,'image'         => $image
      );
    

    Then you can access it like so:

      $chrisburton = twitter_user_info( $twitter_array );
    
      echo $chrisburton->screen_name;
    
  • @traq Couldn't I just use a foreach to return the values as variables?

    http://davidwalsh.name/convert-key-value-arrays-standard-variables-php

  • If you wanted all of them, sure.

    If you only wanted a few, you'd be creating a lot of extra variables for no reason (which would then be floating around, wasting memory, waiting to collide with another var by the same name). I wouldn't recommend it, at least not outside of a function scope.

    If that's really what you want, you can just use extract(), which does exactly the same thing (and offers a bit of control via flags).

    Also, consider that you'd have to make extract()/your function/construct/whatever recursive, or you'd still have arrays:

      <?php
      foreach( $array as $k=>$v ){
          ${$key} = $v;
      }
      echo $nickname;
      // error - $nickname does not exist
      // $auth exists - but it's still an array,
      //  and all those values are still nested inside it.
    

    All things considered, maybe you'd like a user class, that could take the array and organize all the values for you. I'll write up a quick example.

  • I just want something that would be secure but also easy for me to understand at the same time.

  • I think this will do what you want. Try it out (see demo at the bottom).

  • @chrisburton I wanted to learn more about PHP arrays so I played with your example code.

    Maybe you will find it useful.

    http://phpfiddle.org/main/code/9gz-f2n

  • @traq @hotpink Oh, wow. I really appreciate you both taking the time on this.

    @traq I'm receiving this: http://cloud.chrisburton.me/image/420t0i2a3v1B

  • @chrisburton

    $twitter_array needs to be the variable that contains the array you receive from twitter. Are you calling it something else in your code?

    also, does the demo below the class work for you?


    to explain further, undefined variable means that $twitter_array doesn't exist in your code before you try to use it in new twitter_user( $twitter_array ) - which is also why twitter_user is throwing an exception.

  • @traq Yes, I am. $response

    Now I am getting

      ( ! ) Parse error: syntax error, unexpected '[' in C:\Users\Chris\Desktop\wamp\www\auth\example\callback.php on line 186
    

    Line 186: $response = [

  • Now I am getting ( ! ) Parse error: syntax error, unexpected '[' ...

    Sorry. You're not using PHP 5.4, are you?

  • Get rid of my demo code below the class.

    instead, use:

      $cb = new twitter_user( $response );
    

    (assuming $response is your response from twitter).

  • @traq No, I'm using 5.3

    Edit: I already changed that to $response.

  • I updated my demo to be compatible with PHP 5.3. Sorry about that. You can try it again.

    In your own script, did you completely remove my demo code (everything below ## EXAMPLE USAGE ##) and replace it with

       $cb = new twitter_user( $response );
    

    ?

  • @traq I believe so?

           /**
       * wraps info returned from twitter's "sign in with twitter" API.
       * add additional methods/properties as desired and use as a user class.
       *
       * @author Adrian Testa-Avila <github@custom-anything.com>
       * @copyright 2013 Adrian Testa-Avila
       * @license    creative commons attribution-sharealike
       *             http://creativecommons.org/licenses/by-sa/3.0/
       */
      class twitter_user{
    
      ##  NONPUBLIC PROPERTIES  ##
    
          /** 
           * object           this will hold the info you got from the twitter API. 
           *                  "protected" means it can't be accessed directly from outside the object
           *                   (e.g., like `echo $twitter_user->t`).
           *                  we do this so we can control how $t is accessed, using the __get() method below.
           */
          protected $t;
    
      ##  PUBLIC METHODS  ##
    
          /**
           * this method creates a new twitter_user object - 
           *  e.g., like `$chrisburton = new twitter_user( $response );`
           * it simply converts $response to an object and stores it in $this->t.
           * see the __get() method to see how you retrieve values.
           *
           * @param array $twitter_info   the info you got from twitter.
           */
          public function __construct( array $response ){
              if( !empty( $response['auth'] ) && is_array( $response['auth'] ) ){
                  // you might want to verify other|all values exist,
                  // beyond just ['auth'], but that's up to you.
    
                  // converting the array into an object 
                  //  makes it easier to access the values via our __get() method.
                  // (using the json_* functions is an ugly hack, but works well.)
                  $t = json_decode( 
                          json_encode( 
                              $response['auth']
                             ,JSON_BIGINT_AS_STRING|JSON_UNESCAPED_UNICODE 
                          ) 
                      );
                  // make sure there were no errors
                  if( json_last_error() === JSON_ERROR_NONE ){
                      // all good.
                      $this->t = $t;
                  }else{
                      // throw an exception so you know something went wrong
                      throw new Exception( 'conversion to object failed' );
                  }
              }else{
                  // throw an exception so you know something went wrong
                  throw new Exception( '$twitter_info is malformed' );
              }
          }
    
          /**
           * __get() is a "magic method" that runs 
           * whenever you try to access a nonpublic (or nonexistant) property.
           * we're going to use it to access the twitter array.
           * 
           * @param string $name     the name of the value asked for
           * @return mixed           the value that $name is mapped to, if it exists;
           *                         FALSE otherwise
           */
          public function __get( $name ){
              // first check if $name maps directly to a value in the twitter array:
              if( isset( $this->t->$name ) ){
                  // this handles usage like `echo $chrisburton->uid;`
                  return $this->t->$name;
              }
              // next, check if $name maps to a nested value:
              foreach( $this->t as $t ){
                  if( isset( $t->$name ) ){
                      // this handles usage like `echo $chrisburton->nickname;`
                      // or even `echo $chrisburton->urls->twitter;`
                      // (but *not* `echo $chrisburton->twitter;` - that won't work).
                      return $t->$name;
                  }
              }
              // finally, if nothing was found:
              return false;
          }
      }
    
      $cb = new twitter_user( $response );
    
  • @chrisburton

    ...and $response contains the array from twitter (the array you posted at the beginning of this thread)?

    are you still getting the "unexpected [" error?

  • @traq Yes, $response is the main array or whatever you call it.

    I'm getting two errors on line 189 which is $cb = new twitter_user( $twitter_array );

      Notice: Undefined variable: twitter_array in C:\Users\Chris\Desktop\wamp\www\auth\example\callback.php on line 189
    
      Catchable fatal error: Argument 1 passed to twitter_user::__construct() must be an array, null given, called in C:\Users\Chris\Desktop\wamp\www\auth\example\callback.php on line 189 and defined in C:\Users\Chris\Desktop\wamp\www\auth\example\callback.php on line 131
    

    Line 131 is: public function __construct( array $response ){

  • I'm getting two errors on line 189 which is $cb = new twitter_user( $twitter_array );

    Change that to $cb = new twitter_user( $response );

    If you still get an error, use var_dump( $response ) to make sure $response really is the array we're looking for :)

  • @traq Oops, my fault. Now I'm getting a ton of json errors. Will try the var_dump

  • @traq $response is correct. It outputs the following:

      array (size=3)
    'auth' => 
      array (size=5)
        'uid' => int 50799167
        'info' => 
          array (size=6)
            'name' => string 'Chris Burton' (length=12)
            'nickname' => string 'chrisburton' (length=11)
            'urls' => 
              array (size=2)...
    
  • can you zip it all to me?

  • @traq Sent. Thanks again.

  • @chrisburton

    Try putting this

      // create new twitter_user
          $cb = new twitter_user( $response );
          print "Hi, my name is {$cb->name}.<br>Visit me at {$cb->urls->website}";
    

    inside the else{} block where it says

      /**
        * It's all good. Go ahead with your application-specific authentication logic
        */
    

    I'll look more in-depth this afternoon.

  • @traq Hey, no rush. Unless I'm doing something wrong, I'm still getting an error.

  • @chirsburton

    no bother; I'm interested in this too (the whole Opauth/twitter thing).

    Are your errors still the "undefined variable" and "catchable fatal error" on 189? Nothing else?

    And have you tried the twitter_user class again (get a fresh copy, since I fixed the demo), by itself? (Just trying to narrow it down; the class demo works for me, without a hitch.)

  • Okay, I've discovered the problem with my twitter_user class, and provided a solution.

    (1) The flags I use for json_encode() aren't supported in PHP versions less-than 5.4.

    ... The solution is to remove the flags.

    (2) The function json_last_error() did not exist in PHP versions less-than 5.3.

    ... The "solution" (and I use the term loosely) is to remove the error-check and "hope for the best." A better solution is to upgrade your PHP version.

    @chrisburton

    You say you're using 5.3, correct? Simply commenting out this line

      ,JSON_BIGINT_AS_STRING|JSON_UNESCAPED_UNICODE
    

    should solve your problem. If not, you can also replace this line

      if( json_last_error() === JSON_ERROR_NONE ){
    

    with this instead

      if( true ){
    

    I have a working example here, if you'd like to see the result.

  • @traq Wow. I checked my host's PHP version and I'm using PHP Version 5.2.17. I upgraded my localhost version to 5.3 due to a certain feature that gave me an error.

    Really appreciate all of this. After reading some articles, I keep seeing if(isset()) being used for authenticating a twitter user. I'm concerned that I should have stuck with that or is this completely separate?

  • @chrisburton

    Everywhere I see it in that article, isset() (and/or empty()) is being used during the oauth process - e.g., to check what the response returned. The Opauth example does this as well (are you still using the opauth class?).

    Yes, those checks should remain.

    However, don't use them when(if) you're accessing the results via the twitter_user class:

      #no
      if( isset( $twitter_user->name ) ){
          print $twitter_user->name;
      }
    
      #yes
      print $twitter_user->name;
    

    edit

    Also, the code in that article uses the mysql_...() functions.

    ext/mysql is long outdated and will be deprecated in PHP 5.5. It should not be used in new work (try ext/mysqli or PDO instead).

  • @traq I mean, as long as it's secure, I'm not worried about it. I just want to make sure the users info is not vulnerable. It looks like I have a lot to learn. By the way, when you get Kirby up and running, I'd love to see you create a plugin that does this.

    Regarding Opauth class, I'm not sure. What I sent you is what I am still using. I just pasted your code below the code that Opauth comes with.

  • @chrisburton

    Keeping the checks will prevent errors. It may not lead directly to security vulnerabilities, but it definitely makes for better UX and also best coding practice.

    It looks like I have a lot to learn.

    Everyone does. That's the fun part.

    By the way, when you get Kirby up and running, I'd love to see you create a plugin that does this.

    Maybe. I'll have to see how easy it is to integrate.

    I just pasted your code below the code that Opauth comes with.

    which is exactly what I did; works great (that example is on one of my old domains that is still running PHP 5.2). Taking the code you gave me, those minor changes to my class are all it took.

  • @traq - super forum props for helping Chris out with this. I've been following this thread and have admired your dedication to the problem!

  • @TheDoc - thanks very much. I've enjoyed it ...and my own projects may benefit from this as well :)

  • @traq @TheDoc Yes, absolutely. I really appreciate all the help that you have given. I'm glad that we can both benefit from this as well. Thank you for everything.

    My apologies for being MIA for the past couple of days. It's been stressful getting everything ready for this semester of college.

  • You're quite welcome, thank you.

    Just to clarify, is this working for you now?

  • @traq Working 100%. Much easier to write also so that's a definite plus. Thanks again, I owe you one.

  • no prob. cool.