Home > OS >  Object assignment is creating a reference in PHP
Object assignment is creating a reference in PHP

Time:02-03

I have a weird behavior when assigning an existant object to an array. In the example below, I have a class that contains one property. I create a first array with 3 instances (let's call them 2-4-6) and then a second array with 2 other instances using one object of the first array (instance 4). While modifying the values of the objects in second array to create 2 new instances (ie instances 3-5), the instance 4 is also modified. In consequence, at the first array query I get the right values (2-4-6) but after creating the second array I get the modified value (2-5-6). I would expect that the assignment operator copied the object, but instead it creates a reference to the instance. In the example I can get rid of this issue by explicitly calling clone, but in a larger scale, this isn't work (wrong code optimization?). Any clue (or good practice) of how to avoid this issue? Thanks!

<?php
    class TestBase
    {
        private int $m_test = 0;
        public function SetTest(int $v)
        {
            $this->m_test = $v;
        }
        public function GetTest() : int
        {
            return $this->m_test;
        }
    }

    function getNewList(TestBase $ref) : array
    {
        $newlist = [3 => new TestBase(), 5 => $ref];
        $newlist[3]->SetTest(3);
        $newlist[5]->SetTest(5);
        return $newlist;
    }

    $listOfTest = [2 => new TestBase(), 4 => new TestBase(), 6 => new TestBase()];
    $listOfTest[2]->SetTest(2);
    $listOfTest[4]->SetTest(4);
    $listOfTest[6]->SetTest(6);

    foreach ($listOfTest as $test)
    {
        echo $test->GetTest().'<br>';
    }
    // 2
    // 4
    // 6

    //$ref = clone $listOfTest[4];
    $ref = $listOfTest[4];
    $newList = getNewList($ref);

    foreach ($listOfTest as $test)
    {
        echo $test->GetTest().'<br>';
    }
    // 2
    // 5
    // 6
?>

CodePudding user response:

The problem is in the way the PHP uses arrays. Array assignment by reference only works if both the argument and the lvalue are references to arrays. If an object is passed in, it will be copied and a reference to it will be set in both places. The subsequent reassignment of the lvalue will not have any effect on the original array.

Referencing an existing array and then copying it using clone can work around this. The example below shows how this can be done, but you could also use a factory or some other means to create a new array that has references to all of the existing elements (by calling get_object_vars() on each).

<?php
    function getNewList(TestBase $ref) : array
    {
        $newlist = [3 => clone $ref, 5 => clone $ref];
        $newlist[3]->SetTest(3);
        $newlist[5]->SetTest(5);
        return $newlist;
    }

    //$ref = clone $listOfTest[4];
    $ref = &$listOfTest[4];

    var_dump($ref); // TestBase Object (1) (2) { ["m_test"]=> int(5) }

    //$newList = getNewList($ref);

    foreach ($listOfTest as &$test)
    {
        echo "before: ".$test->GetTest().'<br>'; // 2, 4, 6 in output here!

        var_dump($test); // TestBase Object (1) (2) { ["m_test"]=> int(4) }

        echo "after: ".$test->GetTest().'<br>';  // 2, 4, 6 in output here!

        var_dump($test); // TestBase Object (1) (2) { ["m_test"]=> int(4) }
    }
?>

CodePudding user response:

<div >
    <div >
        <nav >
            <ul >
                <li >
                    <div >
                        <span  (click)='click()'></span>
                        <span ></span>
                    </div>
                </li>
                <li >
                    <a href="index.html" ></a>
                </li>
                <li >
                    <a href="#" ></a>
                </li>
            </ul>
            <ul >
    
                <li >
                    <a href="index.html" >
                       <!-- <i ></i> -->
                    </a>
                </li>
                <li >
                    <a href="#" >Mac</a>
                </li>
                <li >
                    <a href="#" >iPad</a>
                </li>
                <li >
                    <a href="#" >iPhone</a>
                </li>
                <li >
                    <a href="#" >Watch</a>
                </li>
                <li >
                    <a href="#" >TV</a>
                </li>
                <li >
                    <a href="#" >Music</a>
                </li>
                <li >
                    <a href="#" >Support</a>
                </li>
                <li >
                    <a href="#" ></a>
                </li>
                <li >
                    <a href="#" ></a>
                </li>
            </ul>
        </nav>
    </div>
</div>



*,
*::after,
*::before {
  padding: 0;
  margin: 0;
}
html {
  font-size: 10px;
  font-family: "SF Pro Display", sans-serif;
}
a {
  display: block;
  text-decoration: none !important;
}
.container {
  max-width: 98rem;
  margin: 0 auto;
  padding: 0 2.2rem;
}

.header {
  position: fixed;
  top: 0;
  z-index: 1400;
  width: 100%;
  height: 4.4rem;
  background-color: rgba(0, 0, 0, 0.8);
  backdrop-filter: blur(2rem);
}

.nav-list {
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 0 -1rem;
}

.nav-list-mobile {
  display: none;
}

.nav-link {
  font-size: 1.4rem;
  color: #fff;
  padding: 0 1rem;
  transition: opacity 0.5s;
}

.nav-link:hover {
  opacity: 0.7;
}

.nav-link-apple {
  width: 1.6rem;
  height: 4.4rem;
  background: url("/assets/apple.svg") center no-repeat;
}

.nav-link-search {
  width: 1.6rem;
  height: 4.4rem;
  background: url("/assets/search.svg") center no-repeat;
}
.nav-link-bag {
  width: 1.4rem;
  height: 4.4rem;
  background: url("/assets/bag.svg") center no-repeat;
}

.iphone-11-pro {
  background: url("/assets/iphone_11_pro_large.jpg");
  margin-top: 4.4rem;
}

.hero {
  height: 69.2rem;
  background-size: auto 73.6rem;
  background-position: center;
  background-repeat: no-repeat;
  border-top: 0.6rem solid #fff;
  border-bottom: 0.6rem solid #fff;
}

.title {
  color: #fff;
  text-align: center;
  padding-top: 5.5rem;
}

.title-heading {
  font-size: 5.6rem;
  line-height: 1.07143;
  letter-spacing: -0.005rem;
}

.title-sub-heading {
  font-size: 2.65rem;
  margin-top: 0.6rem;
  letter-spacing: 0.008rem;
  font-weight: 400;
}

.title-price {
  font-size: 1.7rem;
  color: #888;
  margin-top: 0.68rem;
}

.cta {
  text-align: center;
  margin-top: 1.8rem;
}
.cta-link {
  color: #66bbff;
  font-size: 2.1rem;
  display: inline-block;
}

.cta-link:nth-child(2) {
  margin-top: 2.6rem;
}

.cta-link:hover {
  border-bottom: 2px solid #66bbff;
}

@media screen and (max-width: 1068px) {
  .iphone-11-pro {
    background: url("/assets/iphone_11_pro_medium.jpg");
  }
  .hero {
    height: 65rem;
    background-size: auto 69.8rem;
    background-position: bottom center;
    background-repeat: no-repeat;
    // border-top: .6rem solid #fff;
    // border-bottom: .6rem solid #fff ;
  }
}

@media screen and (max-width: 767px) {
  .header {
    height: 4.8rem;
    transition: background 0.36s cubic-bezier(0.32, 0.68, 0.24, 1),
      height 0.56s cubic-bezier(0.32, 0.68, 0.24, 1);
  }

  .header .container {
    padding: 0;
  }

  .nav-list {
    margin-top: 0;
  }

  .nav-item {
    width: 4.8rem;
    height: 4.8rem;
    display: flex;
    justify-content: center;
  }
  .nav-item-hidden {
    display: none;
  }
  .mobile-menu {
    position: relative;
    z-index: 1500;
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    cursor: pointer;
  }
  .line {
    position: absolute;
    width: 1.7rem;
    height: 1px;
    background-color: #fff;
    transition: margin-top 0.3192s cubic-bezier(0.04, 0.04, 0.12, 0.96);
  }

  .line-top {
    margin-top: 3px;
  }
  .line-bottom {
    margin-top: -0.4rem;
  }
  .active .line-top {
    margin-top: 0;
    transform: rotate(45deg);
    transition: transform 0.3192s 0.1s cubic-bezier(0.04, 0.04, 0.12, 0.96);
  }
  .active .line-bottom {
    margin-top: 0;
    transform: rotate(-45deg);
    transition: transform 0.3192s 0.1s cubic-bezier(0.04, 0.04, 0.12, 0.96);
  }

  .header.active {
    height: 100%;
    background-color: #000;
  }
  .nav-link-apple {
    width: 1.8rem;
    height: 4.8rem;
    position: relative;
    z-index: 1500;

    //
  }
  .nav-link-bag {
    width: 1.8rem;
    height: 4.8rem;
    transition: opacity 3s;
  }

  .nav {
    position: relative;
  }
  .nav-link {
    font-size: 1.7rem;
    padding: 0;
    margin: auto 0;
  }

  .nav-list-larger {
    position: fixed;
    top: 0;
    left: 0;
    width: 0;
    height: 0;
    display: block;
    padding: 10.5rem;
    z-index: 1000;
    opacity: 0;
    box-sizing: border-box;
    transition: opacity 0.3s;
  }

  .nav-list-mobile {
    display: flex;
  }

  .active .nav-list-larger {
    width: 100%;
    height: 100vh;
    opacity: 1;
    visibility: visible;
  }
  .active .nav-list-bag {
    opacity: 0;
    transition: opacity;
  }
  .nav-list-larger .nav-item {
    width: 100%;
    justify-content: flex-start;
    border-bottom: 2px solid rgba(255, 255, 255, 0.1);
  }
  .nav-list-larger .nav-item:nth-child(9) {
    border-bottom: none;
  }

  .active .nav-list-larger .nav-item{
      animation: fadeIn 1s ease-in;
  }
  @keyframes fadeIn {
      from{
          opacity: 0;
      }
      to{
          opacity: 1;
      }
  }
}

 const selectElement =(element:any)=>document.querySelector(element);
    selectElement('.mobile-menu').addeEventListner('click',()=>{
      selectElement('header').classList.toggle('active');
    })
  •  Tags:  
  • Related